NGINX reverse proxy with Rewrites to replace TMG2010 - nginx

I curently have a Forefront TMG2010 doing reverse proxy to several internal web servers. Since it was really easy in TMG I have done lots of host and virtual directory mappings/rewrites.
Now I ened to acheive the same in NGINX.
e.g.,
apphost.domain.com/ needs to map internally to server1/apppath/
apphost.domain.com/test/ needs to map internally to server2/apppath/
apphost.domain.com/dev/ needs to map internally to server3/apppath/
in each case the internal /apppath/ needs to be invisible to the client. This is to achieve the same as the external to internal path mapping in TMG.
What I have done so far is;
server {
listen 80;
server_name host.server.com;
location /test/ {
proxy_set_header Host $host;
rewrite ^(.*)$ /app/$1 break;
proxy_pass http://10.0.0.2;
}
location / {
proxy_set_header Host $host;
rewrite ^(.*)$ /app/$1 break;
proxy_pass http://10.0.0.1;
}
But only the / seems to work, the /test/ doesnt.
Help!

So you want to map /test/foo to http://10.0.0.2/app/foo. The simplest solution is:
location /test/ {
proxy_set_header Host $host;
proxy_pass http://10.0.0.2/app/;
}
According to the documentation the /test/ element will be replaced by /app/ as it passes upstream.

Related

Nginx backslash not supported for proxy_pass (use as a reverse proxy)?

I'm trying to host a 3rd party rather advanced Java app thru Wildfly using NGINX.
I've tried the following things (please check out the commented out config too)
location /app {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8080/app;
proxy_redirect off;
#rewrite ^/$uri $uri break;
#rewrite ^/(.*)/$ /$1 permanent;
}
#location ^~ /app/*\\ {
# proxy_pass http://localhost:8080/app/*\\;
#}
When I go to my hostname/app I can see the complicated Java Web App and login, but when it needs to load some documents it throws out the following XHR error (500 for this URL, but it's actually just 404):
https://example.org/app/app/resource/templateTree/\?count=200&orderBy=-1&orderDirectionAscending=false&fldrTime=-1&hash=53cdac085761b1a3fb5ba4535c8edf85e91c2f
How do I properly rewrite this URL with the backslash? That's exactly what I've tried in the config below, but failed miserably.
The server block itself is defined fine, just under location the rewriting should be added. When I run the application on localhost (without NGINX) I get the following URL in the XHR request, which succeeds:
http://10.0.2.4:8080/app/app/resource/document/home/%5C?count=200&orderBy=-1&orderDirectionAscending=false&fldrTime=0
proxy_pass http://localhost:8080/app$uri; doesn't work at all apparently :(
Wow, all I had to do was remove the /app in proxy_pass http://localhost:8080.
Case closed
bangs hammer

Nginx set browser location root [duplicate]

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 redirects instead of doing reverse proxy with proxy_pass

I have an nginx running on a docker container, which serves a web client.
I want to proxy requests from the client, so that the nginx will pass the POST requests to the server, since the actual destination endpoint is not accessible to the client.
This is the location directive:
location /zipkin {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header Host $host;
proxy_pass http://my-ip:9411/;
}
The client makes requests to http://localhost:8080/zipkin/api/v2/span, which I want the nginx to pass (without redirection) to http://my-ip:9411/api/v2/span.
What happens in practice is that I get a 301 response (to http://localhost:8080/api/v2/span), and the POST is never sent to the destination.
Edit: another try that returns 404 -
location /zipkin/ {
rewrite ^/zipkin/(.*) /$1 break;
proxy_pass http://my-ip:9411$uri; # tried 9411;, 9411/, 9411$uri, 9411$uri/
}
Here $uri is /api/v2/span.
The solution in the end was:
location ~ ^/zipkin(/?)(.*) {
proxy_pass http://my-ip:9411/$2;
}
One of my tries was ^/zipkin(.*) but it evidently didn't work.
Probably the / after the /zipkin is problematic, so (/?) removes it,
And that leaves the (.*) the clean part of the url which needs to be attached to the proxy_passed url.

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