美文网首页
Nginx 301重定向踩坑记录

Nginx 301重定向踩坑记录

作者: EdgeE | 来源:发表于2022-12-05 17:49 被阅读0次

    项目一直使用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里的Locationhttp://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的情况下:

    1. 访问根目录http://10.0.8.2:8000可以自动跳转到http://10.0.8.2:8000/wywy/
    2. 访问资源目录没有带最后的斜杠http://10.0.8.2:8000/wywy,可以自动跳转到http://10.0.8.2:8000/wywy/
    3. 不影响原本ip访问

    了解完原理后,解决方案就简单了:

    1. 直接在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方案,快捷简单。

    2. 使用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/

    3. (新增)无意中发现另外一个现象,网上找了圈资料,没有找到具体解释。使用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> [flag];

    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 
        }
    }
    
    1. 正则部分表示匹配所有,跳转地址是完整的百度首页。

      访问http://10.0.24.204/,结果为:Location: http://www.baidu.com,浏览器跳转到了百度。

    2. 正则部分表示匹配所有,跳转地址手动指定了用户访问的协议、地址端口、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/

    3. 正则部分表示匹配所有,跳转地址为斜杠开头的资源/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/)

    4. 正则部分表示匹配所有,跳转地址为非斜杠开头的资源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/

    5. 正则部分表示,匹配斜杠开头并不以斜杠结尾,跳转地址为绝对路径的/wywy/

      效果同3

    6. 正则部分表示,匹配斜杠开头并不以斜杠结尾,跳转地址为相对路径的wywy/

      效果同4

    相关文章

      网友评论

          本文标题:Nginx 301重定向踩坑记录

          本文链接:https://www.haomeiwen.com/subject/xejifdtx.html