语法
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发现浏览器发送了两次请求:
从日志分析,首先匹配 location ~ /a
,结果被rewrite到了/b
,此时会使用/b
重新匹配所有location
(注意是所有,包括刚匹配过的 /a
,有的文章说是匹配其他的,后续会说明)。接着匹配到了/b,rewrite到了/c,所以继续重新匹配所有location,这一次匹配到了/c,/c中没有继续rewrite,此时nginx通知浏览器rewrite的最终地址是/c,于是浏览器重定向发送请求:http://z.com/c
访问到最终资源。
上面说的每次rewrite会重新匹配所有location,说法是否正确,这里给出证明,我们把上面配置的location /a
和 location /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重定向:
结合日志,我们来分析下这种情况,首先在location /a
中,执行rewrite时,因为有break指令,本次请求结束,访问日志可以看出。所以nginx立即返回当前rewrite的资源,即 /b
,,浏览器就会访问 http://z.com/b
此时匹配到了location /b
在location /b
中,rewrite到了 /c
,所以第二次访问执行了location /b
和location /c
,location /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时,在默认情况下,会先依次执行完。