项目一直使用Nginx作为流量网关,由于网络调整,用户网络跟Nginx服务器网络无法直连,采用了端口映射的方式(服务实际地址为10.0.24.204/wywy,映射后为10.0.8.2:8000/wywy),导致nginx配置出现问题。
问题1: 根目录重定向错误
原NG配置中,设置了根目录 / 重定向到 /wywy。实现浏览器访问http://10.0.24.204时,会自动跳转到http://10.0.24.204/wywy/。原始配置如下:
server {
listen 80;
server_name localhost;
location / {
rewrite ^/$ /wywy/ permanent;
}
location ~ /wywy {
add_header Cache-Control "no-cache, no-store";
root /data/application;
index index.html index.htm;
}
}
但是当使用端口映射后,浏览器访问http://10.0.8.2:8000,并没有跳转到http://10.0.8.2:8000/wywy。NG返回301后,header里的Location为http://10.0.8.2/wywy,注意端口消失了。
问题2: 资源目录重定向错误
不知你注意到没有,当浏览器访问http://10.0.24.204/wywy地址后,会自动在后面加上斜杠/,即变成http://10.0.24.204/wywy/,打开开发者工具(F12),在network标签里,会看到一个301和一个200,两次请求返回:
image-20221129172911675
这是NG主动设置301 Moved Permanently的结果。原理是当用户输入了一个url地址,NG没有找到URL最后部分的资源,并且发现最后部分是一个文件目录(比如wywy是我的目录,里面只有一个index.html),则本次访问的状态码会被设置成301,并在Response header里增加一个Location项,下面会讲这个Location如何取值,这里默认返回的就是http://10.0.24.204/wywy/,增加了一个斜杠/。这时按照配置规则,在wywy/目录下查找index.html资源,于是返回了网页信息。
同样的,当使用端口映射以后,访问新地址http://10.0.8.2:8000/wywy时,会被301重定向到http://10.0.8.2/wywy/,增加斜杠/但端口又消失了。
Nginx重定向配置
nginx默认有三个重定向的参数可以配置:
absolute_redirect:On(开启)时使用绝对URL,并开启下面两个配置参数,共同影响301重定向时Location的取值。Off(关闭)时,使用相对URL作为Location取值。默认开启
server_name_in_redirect:只在absolute_redirect开启时生效。On(开启)时,使用NG配置的server_name。Off(关闭)时,使用用户请求输入URL的服务器部分。默认关闭
port_in_redirect:只在absolute_redirect开启时生效。On(开启)时,加入本地监听的的端口号,但是除443、80外。Off(关闭)时,不加端口号。默认开启
所以,在默认情况下,如果你的NG配置监听的是80或443这两个默认端口,则使用的是绝对URL+用户输入服务器host+无端口号。所以就出现了上面的端口消失情况。
解决方案
先重复下需要达到效果,使用端口映射10.0.8.2:8000 --> 10.0.24.204:80的情况下:
访问根目录http://10.0.8.2:8000可以自动跳转到http://10.0.8.2:8000/wywy/
访问资源目录没有带最后的斜杠http://10.0.8.2:8000/wywy,可以自动跳转到http://10.0.8.2:8000/wywy/
不影响原本ip访问
了解完原理后,解决方案就简单了:
直接在server中设置absolute_redirect参数为Off,这样NG就直接使用相对URL,即保留请求时的host和port,只修改uri部分,最终配置如下:
server {
listen 80;
server_name localhost;
absolute_redirect off;
location / {
rewrite ^/$ /wywy/ permanent;
}
location ~ /wywy/ {
add_header Cache-Control "no-cache, no-store";
root /data/application;
index index.html index.htm;
}
}
该参数是一个全局参数,可能会影响其他规则的运作,造成不可知的问题影响其他服务。配置的时候应该考虑这个因素。
所以当你NG只提供你的一个服务时,可以放心采用1方案,快捷简单。
使用rewrite重写重定向路径,相当于自己定义重定向的规则,不受absolute_redirect配置影响。为了满足第二点需求,需要增加一个location项,最终配置如下:
server {
listen 80;
server_name localhost;
#absolute_redirect on; # 默认为on
location / {
if (-d $request_filename) {
rewrite ^/$ ${scheme}://${http_host}/wywy/ permanent;
}
}
location /wywy {
rewrite ^(.*)$ $scheme://${http_host}${uri}/ permanent;
}
location ~ /wywy/ {
add_header Cache-Control "no-cache, no-store";
root /data/application;
index index.html index.htm;
}
}
使用rewrite,手动指定用户访问根目录/以及/wywy时,要如何跳转到/wywy/。
(新增)无意中发现另外一个现象,网上找了圈资料,没有找到具体解释。使用rewrite重写,当跳转地址是以/开头时,nginx会默认使用绝对路径,并且受到absolute_redirect以及另外两个子参数的影响。此时rewrite时,会去替换整个URI。当把开头的斜杠/去掉时,会默认使用相对路径,并且只会替换请求地址URI的最后一部分。下面讲rewrite会详细解释。
所以实现上述需求跳转时,只要写明地址并不要以/开头即可。具体配置如下:
server {
listen 80;
server_name localhost;
#absolute_redirect on; # 默认为on
location / {
if (-d $request_filename) {
rewrite ^/$ wywy/ permanent;
}
}
location /wywy {
rewrite ^(.*)$ wywy/ permanent;
}
location ~ /wywy/ {
add_header Cache-Control "no-cache, no-store";
root /data/application;
index index.html index.htm;
}
}
Rewrite使用以及参数解释
rewrite的功能就是,结合正则表达式,实现url重写重定向。他可以使用NG的全局变量和自定义变量。关键字位置为:server{}、location{}、if{}
语法: rewrite
regex: 正则表达式
replacement:跳转后地址。可以写完整地址如http://www.baidu.com/somethings,也可以省略前面的协议+host+port部分,如/wywy/或者wywy/。此时当以/开头时,会默认按照规则补全协议、host、port(即绝对路径,并依赖上述absolute_redirect参数的控制),当不以/开头时,会以相对路径进行跳转,注意这里相对的是当前资源的路径,如你访问http://10.0.24.204/abc/def/xyz时,跳转路径会是http://10.0.24.204/abc/def/wywy/
flag:支持的flag标记,共4种last,break,redirect,permanent
四种flag标记
last:本条规则匹配完成后,继续向下匹配新的location URI规则,一般用在 server 和 if 中
break:本条规则匹配完成即终止,不再匹配后面的任何规则,一般使用在 location 中
redirect:返回302临时重定向,浏览器地址会显示跳转后的URL地址
permanent:返回301永久重定向,浏览器地址栏会显示跳转后的URL地址。
NGINX部分保留变量
$scheme: 协议部分,即http、tcp这些
$http_host: 服务器地址,包含端口号
$uri: 去掉协议、地址、端口后的资源相对路径,注意时自带最左边斜杠的。即/wywy
$request_filename: 请求的资源名称,即访问路径最后的部分。如http://10.0.8.2:8000/abc/wywy则表示的是wywy
正则注意项
^表示字符串开头
$表示字符串结尾
[^abc]如果在方括号里面则表示否定、非的意思。
案例解释
这里做一个实验,在nginx的根目录里做rewrite
server {
listen 80;
server_name localhost;
location / {
rewrite ^(.*)$ http://www.baidu.com permanent; # 1
rewrite ^(.*)$ ${scheme}://${http_host}${uri}wywy/ permanent; # 2
rewrite ^(.*)$ /wywy/ permanent; # 3
rewrite ^(.*)$ wywy/ permanent; # 4
rewrite ^/(.*)([^/]*)$ /wywy/ permanent; # 5
rewrite ^/(.*)([^/]*)$ wywy/ permanent; # 6
}
}
正则部分表示匹配所有,跳转地址是完整的百度首页。
访问http://10.0.24.204/,结果为:Location: http://www.baidu.com,浏览器跳转到了百度。
正则部分表示匹配所有,跳转地址手动指定了用户访问的协议、地址端口、uri。
访问http://10.0.24.204/时,结果为:Location: http://10.0.24.204/wywy/
访问http://10.0.24.204/abc时,结果为:Location: http://10.0.24.204/abc/wywy/
访问http://10.0.8.2:8000时,结果为:Location: http://10.0.8.2:8000/wywy/
访问http://10.0.8.2:8000/abc时,结果为:Location: http://10.0.8.2:8000/abcwywy/
正则部分表示匹配所有,跳转地址为斜杠开头的资源/wywy/
访问http://10.0.24.204/时,结果为:Location: http://10.0.24.204/wywy/
访问http://10.0.24.204/abc/xyz时,结果为:Location: http://10.0.24.204/wywy/⚠️注意abc/xyz被覆盖
访问http://10.0.8.2:8000时,结果为:Location: http://10.0.8.2/wywy/ ⚠️注意端口号消失
访问http://10.0.8.2:8000/abc/xyz时,结果为:Location: http://10.0.8.2/wywy/⚠️注意端口号消失,而且abc也被覆盖
如上述rewrite规则,当省略协议、地址端口,资源以斜杠开头时,nginx会根据默认规则默认帮你在斜杠/之前填写上协议、地址和端口。你的请求URI部分会被忽略掉。
如果你没有自定义重定向配置,那规则就是absolute_redirect on 使用绝对URL,server_name_in_redirect off 使用用户请求输入URL的服务器部分,port_in_redirect on 加入本地监听的的端口号,但是除443、80外。由于本地监听了80,所以此处不会增加端口号,所以端口号消失了。
所以就变成了你请求时的协议(http)+ 你请求时的host(10.0.24.204或者10.0.8.2)+你定义的转跳地址(/wywy/)
正则部分表示匹配所有,跳转地址为非斜杠开头的资源wywy/
访问http://10.0.24.204/时,结果为:Location: wywy/
访问http://10.0.24.204/abc时,结果为:Location: wywy/,浏览器跳转地址为http://10.0.24.204/abc/wywy/
访问http://10.0.8.2:8000时,结果为:Location: wywy/, 浏览器跳转地址为http://10.0.8.2:8000/wywy/
访问http://10.0.8.2:8000/abc/xyz时,结果为:Location: wywy/, 浏览器跳转地址为http://10.0.8.2:8000/abc/wywy/
⚠️注意这里相对路径,只针对你访问资源的级别,如最后一个例子,仅仅xyz被替换成wywy/,如果你访问abc/xyz/的话,最终你将得到abc/xyz/wywy/
正则部分表示,匹配斜杠开头并不以斜杠结尾,跳转地址为绝对路径的/wywy/
效果同3
正则部分表示,匹配斜杠开头并不以斜杠结尾,跳转地址为相对路径的wywy/
效果同4