如何在 NGINX 的代理回复中重写 URL

我已经习惯了使用 Apache 和 mod _ xy _ html,并且正在尝试用 NGINX 实现类似的功能。具体的用例是,我在根上下文的服务器端口8080上的 Tomcat 中运行了一个管理 UI:

http://localhost:8080/

我需要在端口80上显示这个文件,但是我在这台主机上运行的 NGINX 服务器上有其他上下文,所以我想尝试访问这个文件:

http://localhost:80/admin/

我希望下面这个超级简单的服务器块可以做到这一点,但事实并非如此:

server {
listen  80;
server_name screenly.local.akana.com;


location /admin/ {
proxy_pass http://localhost:8080/;
}
}

问题是返回的内容(html)包含脚本的 URL 和样式信息,这些都是在根上下文中访问的,所以我需要重写这些 URL,以/admin/而不是/开始。

在 NGINX 我该怎么做?

194850 次浏览

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.