Dynamic proxy_pass based on location - nginx

I'm trying to create dynamic proxy_pass based on location (to be run in docker and redirect traffic to docker stacks hosting http, based on stack name)
Here's my current config. Ive got to the point of working redirect with static location (commented variants at the bottom with blue/green redirect properly and application is working fine). I got problem with dynamic location (~^/(?<ver>.*)/(?<rest>.*)?$) - there's issue with proper glueing of the target URL. When i go to https://myapp.com/blue/, nginx logs issue with dns resolution blue could not be resolved (3: Host not found), looks like it cuts everything after $<ver> in http://${ver}_${subdomain}/$rest. Im not an REGEX expert and i probably messed the location part, any suggestions?
server {
listen 80;
server_name ~^(?<subdomain>.+)\.(mydomain\.com|mydomain\.pl)$;
resolver 127.0.0.11 ipv6=off;
location ~^/(?<ver>.*)/(?<rest>.*)?$ {
set $target http://${ver}_${subdomain}/$rest;
proxy_pass $target;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# location ~ ^/blue/((?<rest>.*))?$ {
# set $exdomain http://blue_$subdomain/$rest;
# proxy_pass $exdomain;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# }
# location ~ ^/green/((?<rest>.*))?$ {
# set $exdomain http://green_$subdomain/$rest;
# proxy_pass $exdomain;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# }
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
send_timeout 90;
}

* in a regular expression is greedy, so $ver will capture everything until the last /, and not the next / as you intended.
One solution is to use the non-greedy variant *? (not to be confused with ?), for example:
location ~ ^/(?<ver>.*?)/(?<rest>.*)?$ { ... }
But a better solution is to use a character class that excludes /, for example:
location ~ ^/(?<ver>[^/]+)/(?<rest>.*)?$ { ... }
See this useful resource on regular expressions.

Related

nginx always adding trailing slash after domain

I've spent forever searching and I can't see that I'm doing anything wrong.
Basically I have two domains and want to route to a backend service depending on which one is visited.
clientkey.local
clientkey.strapi.local
When I visit each of these domains, it's rewriting the urls as:
clientkey.local/
clientkey.strapi.local/
There is no point to adding the extra slashes if I'm just visiting the base domain. I can add the trailing slash if I want and still end up on the home page, but it shouldn't redirect me there automatically. Below is my nginx config.
Any ideas? I'm using docker-compose if that matters. I feel like I'm missing something obvious.
worker_processes 4;
events { worker_connections 1024; }
http {
upstream strapi_servers {
server strapi:1337;
}
upstream gallery_servers {
server gallery;
}
server {
listen 80;
server_name clientkey.strapi.local;
location / {
proxy_pass http://strapi_servers;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
}
server {
listen 80;
server_name clientkey.local;
location / {
proxy_pass http://gallery_servers;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
}
Quick testing reveals that slash is not hidden for non-public TLD websites (Chromium/Chrome).
In other words, if the TLD isn't real, then slash is not being ommited.
example.local - slash shown
example.anyting-non-real - slash shown
example.com - slash hidden

Same location with different proxy urls nginx

We am trying to get nexus via nginx reverse proxy working as a private registry for docker images. We are able to perform all the operations such as pull,search and tag but not able to push to nexus registry .
Below is the nginx configuration under location block.
location ~ ^/(v1|v2)/
{
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https";
proxy_pass http://box.company.net:5555;
proxy_read_timeout 90;
}
We are able to search and pull images.
But with push we face below error.
x.x.x.x - admin [23/Jun/2017:14:32:34 +0800] "POST /v2/fedora/apache/blobs/uploads/?from=fedora%2Fssh&mount=sha256%3Aacd6cf67daf4cd1fcff55ece5a906a45e1569b81271b80136a1f5fecfa4546ed HTTP/1.1" 404 717 "-" "docker/1.12.6 go/go1.7.4 kernel/3.10.0-514.10.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/1.12.6 \x5C(linux\x5C))"
when we try with proxy _pass url as http://box.company.net:4444, we are able to push but cant pull the images .
Is it possible in nginx to pass two different proxy_pass urls under the same location but for different request methods . Any help would be really great ..Thanks
#sempasha : Thanks for you help . It working for me just with a minor tweak .
Below is the location block added to get it working.
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
if ($request_method !~* GET) {
proxy_pass http://box.company.net:4444;
}
if ($request_method = GET) {
proxy_pass http://box.company.net:5555;
}
proxy_read_timeout 90;
}
You can use if or map directives to select backend port.
Note, that If is Evil, not in you case of course.
location ~ ^/(v1|v2)/
{
set $port 5555;
if ($request_method = POST) {
set $port 4444;
}
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https";
proxy_pass http://box.company.net:$port;
proxy_read_timeout 90;
}

Rewrite a subdomain to a backend proxy whith nginx

if have a subdomain on my nginx webserver configuration: sub.mydomain.com
and i have a backend server which listen on port 5000: http://127.0.0.1:5000
is it possible to pass all subdomain calls to the backend?
Like: https://sub.mydomain.com/list to http://127.0.0.1:5000/sub/list
This should work with all methods: POST, PUT, GET, DELETE
UPDATE:
when i call my server: https://mysubdomain.mydomain.com
with the following configuration:
upstream http_backend {
server 127.0.0.1:5000;
}
server_name ~^(?<subdomain>[^.]+)\.mydomain\.com;
This does not work (error: 404):
location / {
proxy_pass http://http_backend/$subdomain/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
This works fine:
location / {
proxy_pass http://http_backend/mysubdomain/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
When i log the $subdomain variable in the access_log, it seems to be correct.
nginx version: nginx/1.9.15
To pass all subdomains you need to set it in server name by putting dot before domain.
server_name .mydomain.com;
Yes, you can use variables in proxy_pass. And you can extract part of domain using regexp server name.
server {
server_name ~^(?<sub>[^.]+)\.example\.com;
# now subdomain of example.com placed to $sub
# please, note, this rule do not work for http://example.com
location / {
proxy_pass http://127.0.0.1:5000/$sub/;
# Path part of proxy_par URI will replace path
# part of location directive (so / -> /$sub/, /xxxx/ -> /$sub/xxxx/)
}
}
Thats all :)
It seems nginx does not add the $uri to the proxy_pass if i use the $subdomain variable.
The following solution works:
location / {
proxy_pass http://http_backend/$subdomain/$uri;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}

nginx redirect all http to https with exceptions

I would like to redirect all http traffic to https with a handful of exceptions. Anything with /exception/ in the url I would like to keep on http.
Have tried the following suggested by Redirect all http to https in nginx, except one file
but it's not working. The /exception/ urls will be passed from nginx to apache for some php processing in a laravel framework but that shouldn't matter.
Any suggestions for improvement much appreciated!
server {
listen 127.0.0.1:80;
location / {
proxy_pass http://127.0.0.1:7080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Accel-Internal /internal-nginx-static-location;
access_log off;
}
location /exception/ {
# empty block do nothing
# I've also tried adding "break;" here
}
return 301 https://localhost$request_uri;
}
Nginx finds the longest matching location and processes it first, but your return at the end of the server block was being processed regardless. This will redirect everything but /exception/ which is passed upstream.
server {
listen 127.0.0.1:80;
access_log off;
location / {
return 301 https://localhost$request_uri;
}
location /exception/ {
proxy_pass http://127.0.0.1:7080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Accel-Internal /internal-nginx-static-location;
}
}

Nginx config: location block only for main page without get paremeters

location ~* ^\/$ {
proxy_pass http://127.0.0.1:8888;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
break;
}
Currently domain.com/?get=parameters goes to port 8888, but need to go to 80 port.
How to complete location block?
Thanks ;)
You can make use of this is_args variable available in nginx. Give it a try the below condition in your above conf. block:
if ($is_args != "") {
proxy_pass http://127.0.0.1:80;
...
...
}

Resources