语法

rewrite reg replacement

例如:

location ~ /z {
         rewrite  ^(.*)$  /zcom/index.html;
         break;
 }

rewrite语法可以放在server,location,if语句块中。

注意事项

  • 如上例中,location匹配所有的以z开头的uri。并且重写到/zcom/index.html ,此时的rewrite会重新再匹配一次,有匹配上了/z,所以如果不加break指令,会出现无限重写的死循环。
  • 假如我们把上例中改为如下:
location ~ /z {
         rewrite  ^(.*)$  /xcom/index.html;
         break;
 }

zcom改为了xcom,并且没有location匹配上xcom,此时会直接去/html目录下找/xcom/index.html。

  • 当location中遇到rewrite指令时,在没有last和break修饰的前提下,执行完当前语句块,会用rewrite后的uri重新匹配一次所有的location;当用break修饰的时候,表示立即返回当前资源,当使用last修饰的时候,表示当前语句块的后续尚未执行的语句都不再执行,立即重新匹配所有location。以下用实例说明:

我们每次访问的链接是:http://z.com/a ,注意每次测试要情况浏览器缓存,否则可能会影响测试结果。

第一种情况:

http {
    log_format  main  '$remote_addr - [$time_local] "$request" '
                      '$status $body_bytes_sent ========$mystr';     
                      /*注意这里加了自定义的变量,来跟踪代码执行路径*/

    access_log  logs/access.log  main;

    server {
        listen       80; 
        server_name  z.com;
        root html;    
        set $mystr ""; 

        location ~ /a {
                set $mystr "1";
                rewrite ^(.*)$ /b;
                set $mystr "$mystr **";           
        }   

        location ~ /b {
                set $mystr '$mystr 2';
                rewrite ^(.*)$ /c; 
        }   

        location ~ /c {
                set $mystr '$mystr 3';
                root /usr/local/nginx;
                index index.html;
        }   

        location ~ /d {
                set $mystr '$mystr 4';
                root /usr/local/nginx;
                index index.html;
        }
    }
   }

访问日志如下:

192.168.159.1 - [06/Aug/2018:19:38:39 +0800] "GET /a HTTP/1.1" 301 185 ========1 ** 2 3
192.168.159.1 - [06/Aug/2018:19:38:39 +0800] "GET /c/ HTTP/1.1" 200 294 ======== 3

浏览器地址改变,发送了两次请求。debuger发现浏览器发送了两次请求:

nginx rewrite 配置后部分浏览器不跳转_html

从日志分析,首先匹配 location ~ /a ,结果被rewrite到了/b,此时会使用/b 重新匹配所有location (注意是所有,包括刚匹配过的 /a,有的文章说是匹配其他的,后续会说明)。接着匹配到了/b,rewrite到了/c,所以继续重新匹配所有location,这一次匹配到了/c,/c中没有继续rewrite,此时nginx通知浏览器rewrite的最终地址是/c,于是浏览器重定向发送请求:http://z.com/c 访问到最终资源。

上面说的每次rewrite会重新匹配所有location,说法是否正确,这里给出证明,我们把上面配置的location /alocation /b 改为如下:

location ~ /a {
                set $mystr "1";
                rewrite ^(.*)$ /ab;   /**原来是重写到/b,这里改为/ab */
                set $mystr "$mystr **";           
        }   
        location ~ /ab {
                set $mystr '$mystr 2';
                rewrite ^(.*)$ /c; 
        }

此时访问 /a 会报错无限循环:

2018/08/06 19:54:07 [error] 1929#0: *49 rewrite or internal redirection cycle while processing "/ab", client: 192.168.159.1, server: z.com, request: "GET /a HTTP/1.1", host: "z.com"

假如每次rewrite是继续匹配后续的location,根据上面的配置,会继续匹配到/ab,然后uri被重写到、为/c,肯定不会出现死循环。

第二种情况— 添加last指令

http {
    log_format  main  '$remote_addr - [$time_local] "$request" '
                      '$status $body_bytes_sent ========$mystr';     
                      /*注意这里加了自定义的变量,来跟踪代码执行路径*/

    access_log  logs/access.log  main;

    server {
        listen       80; 
        server_name  z.com;
        root html;    
        set $mystr ""; 

        location ~ /a {
                set $mystr "1";
                rewrite ^(.*)$ /b last; /**唯一的不同就是添加了last*/
                set $mystr "$mystr **";           
        }   

        location ~ /b {
                set $mystr '$mystr 2';
                rewrite ^(.*)$ /c; 
        }   

        location ~ /c {
                set $mystr '$mystr 3';
                root /usr/local/nginx;
                index index.html;
        }   

        location ~ /d {
                set $mystr '$mystr 4';
                root /usr/local/nginx;
                index index.html;
        }
    }
   }

访问日志如下:

192.168.159.1 - [06/Aug/2018:20:01:43 +0800] "GET /a HTTP/1.1" 301 185 ========1 2 3
192.168.159.1 - [06/Aug/2018:20:01:43 +0800] "GET /c/ HTTP/1.1" 200 294 ======== 3

我们发现 location /a中rewrite语句后的语句没有执行,因为 ** 没有输出。所以last的指令的用途就是停止执行rewrite的后续语句。其他跟默认情况没什么区别。

情况三 — 添加break

http {
    log_format  main  '$remote_addr - [$time_local] "$request" '
                      '$status $body_bytes_sent ========$mystr';     
                      /*注意这里加了自定义的变量,来跟踪代码执行路径*/

    access_log  logs/access.log  main;

    server {
        listen       80; 
        server_name  z.com;
        root html;    
        set $mystr ""; 

        location ~ /a {
                set $mystr "1";
                rewrite ^(.*)$ /b break; /**唯一的不同就是添加了break*/
                set $mystr "$mystr **";           
        }   

        location ~ /b {
                set $mystr '$mystr 2';
                rewrite ^(.*)$ /c; 
        }   

        location ~ /c {
                set $mystr '$mystr 3';
                root /usr/local/nginx;
                index index.html;
        }   

        location ~ /d {
                set $mystr '$mystr 4';
                root /usr/local/nginx;
                index index.html;
        }
    }
   }

访问日志如下:

192.168.159.1 - [06/Aug/2018:20:06:06 +0800] "GET /a HTTP/1.1" 301 185 ========1
192.168.159.1 - [06/Aug/2018:20:06:06 +0800] "GET /b/ HTTP/1.1" 301 185 ======== 2 3
192.168.159.1 - [06/Aug/2018:20:06:06 +0800] "GET /c/ HTTP/1.1" 200 294 ======== 3

我们可以看到浏览器发送了三次请求,前两个都是301重定向:

nginx rewrite 配置后部分浏览器不跳转_html_02

结合日志,我们来分析下这种情况,首先在location /a中,执行rewrite时,因为有break指令,本次请求结束,访问日志可以看出。所以nginx立即返回当前rewrite的资源,即 /b,,浏览器就会访问 http://z.com/b 此时匹配到了location /blocation /b中,rewrite到了 /c,所以第二次访问执行了location /blocation /clocation /c中没有rewrite了,所以nginx把rewrite的最终结果/c返回给浏览器,浏览器于是重定向到http://z.com/c,此时匹配location /c,得到最终结果。

情况四— 多重rewrite语句

location ~ /a {
         set $mystr "1";
         rewrite ^(.*)$ /b ;
         set $mystr "$mystr **";
         /**添加了下面两句*/
         rewrite ^(.*)$ /c; 
         set $mystr "$mystr ** ";

 }

访问日志如下:

192.168.159.1 - [06/Aug/2018:20:22:31 +0800] "GET /a HTTP/1.1" 301 185 ========1 ** **  3
192.168.159.1 - [06/Aug/2018:20:22:31 +0800] "GET /c/ HTTP/1.1" 200 294 ======== 3

所以当一个location中有多个rewrite时,在默认情况下,会先依次执行完。