今天遇到一个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
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重写的形式带上端口号,问题解决。