语法
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时,在默认情况下,会先依次执行完。
本文详细解析 Nginx 中 rewrite 指令的工作原理及其使用技巧,通过具体实例介绍了如何利用 rewrite 进行 URL 重写,以及 last 和 break 修饰符的作用。
1966

被折叠的 条评论
为什么被折叠?



