Nginx case insensitive URL and rewrite URL? - nginx

I have Nginx configured as reverse proxy
server {
listen 80;
server_name www.pluto.com;
location / {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8080;
}
}
I need that the URL request from any combination of FOLDER1 (case insensitive) is rewrite from URL
http://www.pippo.com/FOLDER1/etc..etc..etc..
to (always lowercase folder1)
http://127.0.0.1/folder1/etc..etc..etc...
where etc..etc..etc. = anything that I need to keep
How can I do?

location ~* ^/folder1(?:/?)(.*) {
proxy_pass http://127.0.0.1:8080/folder1/$1$is_args$args;
}
(?:/?) says we may have one slash after folder1 which in that case we just ignore it. we then grab whatever remains by (.*) which will be placed in $1. then in the proxy_pass argument, we build the new URI: first we add what we had after folder1/ in the original URI and then we add all the arguments. so if your original URI is /folder1/test, the proxy_pass URI should be http://127.0.0.1:8080/folder1/test. do some experiments with the cases where you have parameters, e.g. /folder1/test?id=1. I'm not sure whether (.*) will capture the ?id=1 part too or not. if it does, then the proxy_pass should be changed to http://127.0.0.1:8080/folder1/$1.

Related

Not able to see URL without trailing slash in NGINX

I know that this is a common issue in NGINX and there are many threads about that.
Issue:
When accessing the URL http://localhost/var without trailing slash is working with my current config. However, I need to add 2 locations (one with trailing slash and second one without trailing slash).
If i try accessing the URL with trailing slash, it is being redirected correctly and page is displayed correctly.
The issue comes when i try accessing the second URL:
http://localhost/var/api/app/v2
I did the same, adding two locations (one with trailing slash and second one without trailing slash). However, looks like the rewrite that i included inside server block is making a conflict as the url has some stuff behind "var". I am getting an 404 error.
This is my current config file:
server {
listen 8080;
listen [::]:8080;
server_name localhost;
rewrite ^/(.*)/$ /$1 permanent;
absolute_redirect off;
access_log /var/log/nginx/access.log main;
##location 1##
location /##var##/ {
set $dash local.##var##.svc;
rewrite ^/##var##(.*)$ $1 break;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://$dash:8080;
auth_basic_user_file /etc/apache2/dash_auth;
}
location /##var## {
set $dash local.##var##.svc;
rewrite ^/##var##/(.*)$ $1 break;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://$dash:8080/;
auth_basic_user_file /etc/apache2/dash_auth;
}
##location 2##
location /##var##/api/app/v2/ {
set $nu local2.##var##.svc;
rewrite ^/##var##/api/v2/app/(.*)$ /app/$1 break;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://$nu:6000;
auth_basic_user_file /etc/apache2/dash_auth;
}
location /##var##/api/app/v2 {
set $nu local2.##var##.svc;
rewrite ^/##var##/api/app/v2(.*)$ /app/$1 break;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://$nu:6000/;
auth_basic_user_file /etc/apache2/dash_auth;
}
}
This is working when accessing the URL:
http://localhost/var
but not when accessing the URL:
http://localhost/var/api/app/v2
i think the conflict is regarding the rewrite in the server but not sure how can i fix that.
I tried to add the rewrite inside the location for /var/ but not working as expected. I was thinking about include a specifyc rewrite for every location but not sure if this is gonna work.
Also I trid to add these two rewite rules in server block, but not working:
rewrite ^/api/app/(.*)/$ /api/app/$1 permanent;
rewrite ^/(.*)/$ /$1 permanent;
Regarding the ports, that is something currently working. i mean without including any modification in the nginx config it is working (just with URL with trailing slash although).
Change
proxy_pass http://$dash:8080;
To
proxy_pass http://$dash:8080/;
Also your default nginx location should looke like this
location / {
auth_basic_user_file /etc/apache2/dash_auth;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://$dash:8080/;
}

More accurate nginx location matching

I have the following nginx rule
location /api {
proxy_pass http://flask:5000/api;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
It will match the following path, which is what I'm expecting
http://localhost/api
http://localhost/api/
http://localhost/api/a
http://localhost/api?
http://localhost/api?name=value
However, it will also match the following, which I'm not expecting
http://localhost/apii
http://localhost/apiX
May I know how can I avoid from matching unwanted http://localhost/apii and http://localhost/apiX and http://localhost/apiXX and ...
This requires two location blocks - one matching specifically /api and one for everything in the path /api/. In this way urls like /apio will not be captured.
Example:
location = /api {
proxy_pass http://flask:5000/api;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#Querystrings and /api, matches the first, any /api/* matches this one
location /api/ {
proxy_pass http://flask:5000/api;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Reference http://nginx.org/en/docs/http/ngx_http_core_module.html#location
One possibility is to use a regular expression. As the proxy_pass is not actually transforming the URI, the uri element can be safely removed.
location ~ ^/api($|/) {
proxy_pass http://flask:5000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Note that regular expression location blocks are evaluated in order. See this document for details.
It's actually really simple.
Just change location /api { to location /api/ {
Nginx will add a trailing slash to any request ending /api so there is no need to add a separate block for them. This block will behave as you expected your original one to.

forward matched specific location and proxy redirect it in nginx

I am trying to forward specific uri if matched to backend in nginx. For example
forward https://www.hostname.com/*/a/b/c to https://int.hostname.com/*/a/b/c (Where * is a variable auto populated from regex)
Current configuration looks like below and have no idea how to proceed on above
location /a/b/c/ {
proxy_set_header Host $proxy_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 180m;
proxy_pass http://int.hostname.com/v2/e/t/a/b/c;
proxy_redirect default;
}
If you want your location to match any URI that ends with /a/b/c/, you will need to use regular expression syntax. See this document for details.
The URI suffix of the proxy_pass statement is optional. In the absence of a URI suffix, the original URI will be passed unmodified. See this document for details.
For example:
location ~ /a/b/c/$ {
...
proxy_pass http://int.hostname.com;
...
}

How do I rewrite URLs in a proxy response in NGINX

I'm used to using Apache with mod_proxy_html, and am trying to achieve something similar with NGINX. The specific use case is that I have an admin UI running in Tomcat on port 8080 on a server at the root context:
http://localhost:8080/
I need to surface this on port 80, but I have other contexts on the NGINX server running on this host, so want to try and access this at:
http://localhost:80/admin/
I was hoping that the following super simple server block would do it, but it doesn't quite:
server {
listen 80;
server_name screenly.local.akana.com;
location /admin/ {
proxy_pass http://localhost:8080/;
}
}
The problem is that the returned content (html) contains URLs to scripts and style info that is all accessed at the root context, so I need to get these URLs rewritten to start with /admin/ instead of /.
How do I do this in NGINX?
We should first read the documentation on proxy_pass carefully and fully.
The URI passed to upstream server is determined based on whether "proxy_pass" directive is used with URI or not. Trailing slash in proxy_pass directive means that URI is present and equal to /. Absense of trailing slash means hat URI is absent.
Proxy_pass with URI:
location /some_dir/ {
proxy_pass http://some_server/;
}
With the above, there's the following proxy:
http:// your_server/some_dir/ some_subdir/some_file ->
http:// some_server/ some_subdir/some_file
Basically, /some_dir/ gets replaced by / to change the request path from /some_dir/some_subdir/some_file to /some_subdir/some_file.
Proxy_pass without URI:
location /some_dir/ {
proxy_pass http://some_server;
}
With the second (no trailing slash): the proxy goes like this:
http:// your_server /some_dir/some_subdir/some_file ->
http:// some_server /some_dir/some_subdir/some_file
Basically, the full original request path gets passed on without changes.
So, in your case, it seems you should just drop the trailing slash to get what you want.
Caveat
Note that automatic rewrite only works if you don't use variables in proxy_pass. If you use variables, you should do rewrite yourself:
location /some_dir/ {
rewrite /some_dir/(.*) /$1 break;
proxy_pass $upstream_server;
}
There are other cases where rewrite wouldn't work, that's why reading documentation is a must.
Edit
Reading your question again, it seems I may have missed that you just want to edit the html output.
For that, you can use the sub_filter directive. Something like ...
location /admin/ {
proxy_pass http://localhost:8080/;
sub_filter "http://your_server/" "http://your_server/admin/";
sub_filter_once off;
}
Basically, the string you want to replace and the replacement string
You may also need the following directive to be set before the first "sub_filter" for backend-servers with data compression:
proxy_set_header Accept-Encoding "";
Otherwise it may not work.
For your example it will look like:
location /admin/ {
proxy_pass http://localhost:8080/;
proxy_set_header Accept-Encoding "";
sub_filter "http://your_server/" "http://your_server/admin/";
sub_filter_once off;
}
You can use the following nginx configuration example:
upstream adminhost {
server adminhostname:8080;
}
server {
listen 80;
location ~ ^/admin/(.*)$ {
proxy_pass http://adminhost/$1$is_args$args;
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;
}
}
If there are no hyperlinks which need to be rewritten with sub_filter,
you might just use the proxy_redirect directive:
location /admin/ {
proxy_pass http://localhost:8080/;
proxy_redirect / /admin/
}
It changes the Location-Header of the response according to the given 'match-rewrite' rule.

Nginx pass_proxy subdirectory without url decoding

I need to write an nginx location directive to proxy requests to subdirectory to another server preserving urlencoding and removing subdirectory prefix.
Here's an artificial example — request like this:
http://1.2.3.4/api/save/http%3A%2F%2Fexample.com
should pass as
http://abcd.com/save/http%3A%2F%2Fexample.com
I tried several different ways. Here're couple of them:
From this SO question
location /api/ {
rewrite ^/api(/.*) $1 break;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://abcd.com;
}
But it decodes the string, so http://abcd.com gets /save/http://example.com
From another SO question
location /api/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://abcd.com;
}
But it keeps subdirectory, so http://abcd.com gets /api/save/http%3A%2F%2Fexample.com.
What's needed is somewhere in the middle. Thank you!
UPD: Here's a ticket in nginx bug tracker
But there is no easy way to fix this nginx behaviour. There are some bugs in nginx trac, you could add yours. trac.nginx.org/nginx/…. So, I think that the simplest way is to have subdomain. – Alexey Ten Feb 24 '15 at 14:49
https://trac.nginx.org/nginx/ticket/727
If you want nginx to do something custom, you can do so using ​proxy_pass with variables (and the $request_uri variable, which contains original unescaped request URI as sent by a client). In this case it will be your responsibility to do correct URI transformations. Note though that this can easily cause security issues and should be done with care.
Challenge accepted!
location /api/ {
rewrite ^ $request_uri;
rewrite ^/api/(.*) $1 break;
return 400;
proxy_pass http://127.0.0.1:82/$uri;
}
That's it, folks!
Here's for the full proof.
The config file for nginx/1.2.1:
server {
listen 81;
#first, the solution
location /api/ {
rewrite ^ $request_uri;
rewrite ^/api/(.*) $1 break;
return 400; #if the second rewrite won't match
proxy_pass http://127.0.0.1:82/$uri;
}
#next, a few control groups
location /dec/ {
proxy_pass http://127.0.0.1:82/;
}
location /mec/ {
rewrite ^/mec(/.*) $1 break;
proxy_pass http://127.0.0.1:82;
}
location /nod/ {
proxy_pass http://127.0.0.1:82;
}
}
server {
listen 82;
return 200 $request_uri\n;
}
Here are the results of running the queries for each location:
% echo localhost:81/{api,dec,mec,nod}/save/http%3A%2F%2Fexample.com | xargs -n1 curl
/save/http%3A%2F%2Fexample.com
/save/http:/example.com
/save/http:/example.com
/nod/save/http%3A%2F%2Fexample.com
%
Note that having that extra return 400; is quite important — otherwise, you risk having a security issue (file access through //api etc), as Maxim has briefly mentioned in your trac ticket.
P.S. If you think using the rewrite engine as a finite-state automaton is super cool, you might also want check out my http://mdoc.su/ project, or fork it github.
What you have to do is fairly easy as long as we are talking prefix matching with ^~ or no modifier
location /api/ {
# if you don't want to pass /api/ add a trailing slash to the proxy_pass
proxy_pass http://localhost:8080/;
...
}
And everything will be passed along without decoding, you don't have to pass $uri
Also while you use proxy pass you should also set these headers
# pass headers and body along
proxy_pass_request_headers on;
proxy_pass_request_body on;
# set some headers to make sure the reverse proxy is passing along everything necessary
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Resources