在Nginx内部自动处理3XX跳转

利用Nginx很容易的配置反向代理和负载均衡的服务, 比如下面的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
upstream backends {
server 10.0.0.10:8080;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
}
server{
listen 8080;
location / {
proxy_pass http://backends;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

它将客户端的请求转发给后台的三个服务器。 负载均衡的算法又多种, 比如轮询、least_conn、ip_hash、weight等算法,本文重点不介绍这方面的内容,而是下面的需求。

后端服务器可能返回 3XX的redirect的response, Nginx会把这个请求直接返回给客户端。现在我们的需求是让Nginx自己处理这个跳转,而客户端无感知。

经过查找,找到一种解决方案, 如 serverfault提到的,我们可以利用error_page指令将3xx转发给另外的location, 利用$upstream_http_location获得转发的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
upstream backends {
server 10.0.0.10:8080;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
}
server{
listen 8080;
location / {
proxy_pass http://backends;
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_intercept_errors on;
error_page 301 302 307 = @handle_redirect;
}
location @handle_redirect {
set $saved_redirect_location '$upstream_http_location';
proxy_pass $saved_redirect_location;
}
}

挺巧妙的一个解决方案。

另一个解决方案是 stackoverflow提到的, 返回给客户端的时候, 利用proxy_redirect指令修改Location的头,让client端重定向到Nginx特定的地址,Nginx再将请求转发给后端服务器。, 不过这个方案要求客户端支持重定向的能力。

参考文档
  1. https://serverfault.com/questions/423265/how-to-follow-http-redirects-inside-nginx
  2. https://stackoverflow.com/questions/20254456/intercepting-backend-301-302-redirects-proxy-pass-and-rewriting-to-another-loc
  3. https://stackoverflow.com/questions/42134258/follow-redirect-301-and-proxy-pass-last-location-found-with-nginx
  4. https://gist.github.com/sirsquidness/710bc76d7bbc734c7a3ff69c6b8ff591
  5. http://www.jianshu.com/p/ac8956f79206
  6. https://github.com/moonbingbing/openresty-best-practices/blob/master/ngx/reverse_proxy.md