Nginx中处理重定向端口丢失问题
今天遇到一个nginx重定向的问题,应用通过重定向访问nginx的80端口的Html静态服务,在重定向时导致端口丢失现象,通过网络查找资料记之如下。
nginx有的时候并不像Apache那样智能,对于redirect location的处理尤为惨淡,几乎只能用户手工处理非标准端口的问题。
比如因为种种原因,nginx并不能监听在80端口,或者外部通过NAT方式将请求丢给nginx,外部地址并不是标准http(s)端口,此时nginx并不能美好的处理这些重定向。发生重定向的时候会丢失端口。
比如以下两种配置方案:
#反向代理 listen 81 default_server; set $TOMCAT_HOME /var/lib/tomcat7; location / { root $TOMCAT_HOME/webapps/ROOT; proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
# 访问/data的时候,浏览器发出请求`http://xxx.com/data` # 未查询到/data文件,url被重定向到`http://xxx.com/data/`目录 listen 81 default_server; location /data { root /var/data; }
浏览器请求的时候会发现只要发生重定向,端口号就会丢失,导致浏览器访问到错误的端口。
分别对这两种情况给出解决方案。
反向代理
这个很容易搞定,那一堆 proxy_set_header
不知道何时在网上流传开来的,几乎国内外文档无一例外都是这个鸟样子。
后来我发现gitlab-ce
这个用非标准端口访问是没有问题的,我看了一下 gitlab -ce 的nginx配置,发现是这么配置的:
proxy_set_header Host $http_host;
我又发现nginx软件包释放出的配置文件,发现里面其实是带有一个参考文件 /etc/nginx/proxy_params
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 $scheme;
这里面写的也是 proxy_set_header Host $http_host;
,于是乎直接include,搞定
location / { root $TOMCAT_HOME/webapps/ROOT; proxy_pass http://127.0.0.1:8080/; include proxy_params; }
没这个文件就把这些内容手工敲到location
配置段下。
再细看官方文档,其实也提及了:
An unchanged “Host” request header field can be passed like this: proxy_set_header Host $http_host;
访问目录没带 /
这个比较棘手,比如 $document_root
存在 data/index.html
文件,但是访问的时候使用的是/data
最后没加/
去表示其访问的是一个目录,
服务器此时会进行301 Moved Permanently
永久重定向处理,
但是比较扯的地方在于,如果nginx监听的是非标准端口,这个301返回的 Location
没有端口号,导致浏览器请求出错。
用curl可以很明显的看到这一点:
$ curl -v mydomain:81/data * Hostname was NOT found in DNS cache * Trying *.*.*.*... * Connected to mydomain (*.*.*.*) port 81 (#0) > GET /test HTTP/1.1 > User-Agent: curl/7.35.0 > Host: mydomain:81 > Accept: */* > < HTTP/1.1 301 Moved Permanently * Server nginx is not blacklisted < Server: nginx < Date: Wed, 18 Nov 2015 07:39:03 GMT < Content-Type: text/html < Content-Length: 178 < Connection: keep-alive < Location: http://mydomain/data/ < <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx</center> </body> </html> * Connection #0 to host mydomain left intact
可以很明显的看到 Location
没有端口号了,这个重定向又和反向代理不一样。于是遍寻google,最终在stackoverflow的问答中找到了解决方案:
if (-d $request_filename) { rewrite [^/]$ $scheme://$http_host$uri/ permanent; }
通过配置对URL重写的形式带上端口号,问题解决。