Nginx 关于 Rewrite 执行顺序详解

第一篇: break 和 last 的区别

Rewrite 模块概述

REFER:   http://wiki.nginx.org/NginxHttpRewriteModule#rewrite
If the directives of this module are given at the server level, then they are carried out before the location of the request is determined. If in that selected location there are further rewrite directives, then they also are carried out. If the URI changed as a result of the execution of directives inside location, then location is again determined for the new URI.
This cycle can be repeated up to 10 times, after which Nginx returns a 500 error.
Rewrite( URL 重写)指令可以出现在 server{} 下,也可以出现在 location{} 下,它们之间是有区别的!对于出现在 server{} 下的 rewrite 指令,它的执行会在 location 匹配之前;对于出现在 location{} 下的 rewrite 指令,它的执行当然是在 location 匹配之后,但是由于 rewrite 导致 HTTP 请求的 URI 发生了变化,所以 location{} 下的 rewrite 后的 URI 又需要重新匹配 location ,就好比一个新的 HTTP 请求一样(注意由 location{} 内的 rewrite 导致的这样的循环匹配最多不超过 10 次,否则 nginx 会报 500 错误)。总的来说,如果 server{} 和 location{} 下都有 rewrite ,依然是先执行 server{} ,然后进行 location 匹配,如果被匹配的 location{} 之内还有 rewrite 指令,那么继续执行 rewrite ,同时因为 location{} 内的 rewrite 改变了 URI ,那么重写后的结果 URI 需要当做一个新的请求,重新匹配 location (应该也包括重新执行 server{} 下的 rewrite 吧)。

Last 与 break flag 的区别

关于 last flag 和 break flag 的区别,官方文档的描述是:“ last - completes processing of rewrite directives, after which searches for corresponding URI and location ”和“ break - completes processing of rewrite directives ”,都有“不让继续执行后面的 rewrite 指令”的含义,但是两者的区别并没有展开。
这里我用实验来告诉大家区别。实验准备:
1、  安装 nginx ;(如果对安装和 location 不了解的,请参考: http://eyesmore.iteye.com/blog/1141660 )
2、  在 nginx 安装目录的 html 子目录下创建 4 个文件,分别叫: aaa.html , bbb.html , ccc.html 和 ddd.html ,文件内容分别是各自的文件名(例 aaa.html 文件内容不妨写 aaa html file )。
3、  Nginx 配置文件初始化是:
error_log  logs/error.log info;  #URL 重写模块的日志会写入此文件
server {
listen       9090;
server_name  localhost;
root html;
rewrite_log on;   # 打开 URL 重写模块的日志开关,以便写入 error_log
location  /aaa.html {
            rewrite "^/aaa\.html$"  /bbb.html;
            rewrite "^/bbb\.html$"  /ddd.html;
        }  
location  /bbb.html {
            rewrite "^/bbb\.html$" /ccc.html;
        }  
}
上述配置注意两点: 1 、打开 rewrite 模块的日志开关,以便 rewrite 执行日志写入 error_log (注: rewrite 日志写入 error_log 的级别是 notice ,所以要注意 error_log 日志级别,此处用 info ); 2 、定义了两个 location ,分别是 /aaa.html 和 /bbb.html ,但是在 /aaa.html 中,把 /aaa.html 重写成 /bbb.html ,接着又把 /bbb.html 重写成 /ddd.html ;在 /bbb.html 中,把 /bbb.html 重写成 /ccc.html 。
[ 测试 1] 没有 last 和 break 标记时:请求 aaa.html
[root@web108 ~]# curl http://localhost:9090/aaa.html
ddd html file
[root@web108 ~]#
Error_log 的日志内容:
2011/08/07 22:13:23 [notice] 9066#0: *85 "^/aaa\.html$" matches "/aaa.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [notice] 9066#0: *85 rewritten data: "/bbb.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [notice] 9066#0: *85 "^/bbb\.html$" matches "/bbb.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [notice] 9066#0: *85 rewritten data: "/ddd.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [info] 9066#0: *85 client 127.0.0.1 closed keepalive connection
URL 重写模块的日志告诉我们:对于一个 HTTP 请求“ GET /aaa.html ”,重写过程是:先 /aaa.html 被重写为 /bbb.html ;然后 rewritten data: /bbb.html ,继续执行后面的 rewrite 指令,进而被重写为 /ddd.html ,然后 rewrittern data: /ddd.html 后面没有重写了(其实此时 /ddd.html 需要再次重新匹配 location 的,只是日志没有体现出来,接下来的测试 2 会体现这点),于是输出 /ddd.html 的内容。
[ 测试 2] 使用 last 标记时:请求 aaa.html
将上述 location /aaa.html {} 修改成:
location  /aaa.html {
rewrite "^/aaa\.html$"  /bbb.html   last ;
rewrite "^/bbb\.html$"  /ddd.html;
}  
测试结果:
[root@web108 ~]# curl http://localhost:9090/aaa.html
ccc html file
[root@web108 ~]#
Error_log 日志:
2011/08/07 22:24:31 [notice] 18569#0: *86 "^/aaa\.html$" matches "/aaa.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [notice] 18569#0: *86 rewritten data: "/bbb.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [notice] 18569#0: *86 "^/bbb\.html$" matches "/bbb.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [notice] 18569#0: *86 rewritten data: "/ccc.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [info] 18569#0: *86 client 127.0.0.1 closed keepalive connection
不知道读者看到 GET /aaa.html 显示的结果“ ccc html file ”会不会惊讶:“为什么结果不是 bbb html file ”。下面解释下整个过程:首先 /aaa.html 匹配了 location /aaa.html {} ,于是执行 rewrite "^/aaa\.html$"  /bbb.html last ,把 /aaa.html 重写为 /bbb.html ,同时由于 last flag 的使用,后面的 rewrite 指令(指的是 rewrite "^/bbb\.html$"  /ddd.html )不会被执行。似乎此时应该输出“ bbb html file ”才对,但是我们看看 nginx 官方解释:“ last - completes processing of rewrite directives, after which searches for corresponding URI and location ”意思是说 last 不再匹配后面的 rewrite 指令,但是紧接着需要对重写后的 URI 重新匹配 location 。让我们再看看官方的“ If the directives of this module are given at the server level, then they are carried out before the location of the request is determined. If in that selected location there are further rewrite directives, then they also are carried out. If the URI changed as a result of the execution of directives inside location, then location is again determined for the new URI. This cycle can be repeated up to 10 times, after which Nginx returns a 500 error. ”因此,重新匹配的时候,匹配到了新的 location /bbb.html {} ,执行“ rewrite "^/bbb\.html$" /ccc.html ”,最后的内容是“ ccc html file ”。
[ 测试 3] 使用 break 标记时:请求 aaa.html
将上述 location /aaa.html {} 修改成使用 break 标记:
location  /aaa.html {
rewrite "^/aaa\.html$"  /bbb.html  break ;
rewrite "^/bbb\.html$"  /ddd.html;
}  
测试结果:
[root@web108 ~]# curl http://localhost:9090/aaa.html
bbb html file
[root@web108 ~]#
日志结果:
2011/08/07 22:37:49 [notice] 21069#0: *89 "^/aaa\.html$" matches "/aaa.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:37:49 [notice] 21069#0: *89 rewritten data: "/bbb.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:37:49 [info] 21069#0: *89 client 127.0.0.1 closed keepalive connection
我想这个结果不用多做解释了,充分体现了 break 和 last 的区别:“ last - completes processing of rewrite directives, after which searches for corresponding URI and location ”和“ break - completes processing of rewrite directives ”。 Break 和 last 都能阻止继续执行后面的 rewrite 指令,但是 last 如果在 location 下用的话,对于重写后的 URI 会重新匹配 location ,但是 break 则不会重新匹配 location 。简单的说, break 终止的力度比 last 更加彻底(为了记忆的方便,我们可以把重新后的 URI 重新匹配 location 理解为“ URI 匹配 location 的循环语句的下一次迭代”,高级程序设计里面 break 一般用做退出循环,所以 break 不仅终止继续执行 rewrite ,而且退出 URI 重新匹配 location 的循环迭代)。

Nginx 关于 Rewrite 的迭代 第二篇

例题 1

配置:
error_log  logs/error.log info;
server {
listen       9090;
server_name  localhost;
root html;
rewrite_log on;
rewrite "^/aaa\.html$"  /bbb.html;
location  /ccc.html {
rewrite "^/ccc\.html$"  /eee.html;
}
location  /bbb.html {
rewrite "^/bbb\.html$" /ccc.html;
rewrite "^/ccc\.html$" /ddd.html;
}  
}   
结果:
[root@web108 ~]# curl http://localhost:9090/aaa.html
ddd html file
[root@web108 ~]#
日志:
2011/08/08 10:05:41 [notice] 31592#0: *90 "^/aaa\.html$" matches "/aaa.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 rewritten data: "/bbb.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 "^/bbb\.html$" matches "/bbb.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 rewritten data: "/ccc.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 "^/ccc\.html$" matches "/ccc.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 rewritten data: "/ddd.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 "^/aaa\.html$" does not match "/ddd.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [info] 31592#0: *90 client 127.0.0.1 closed keepalive connection
解释:
GET /aaa.html 请求,首先执行 server 级的 rewrite 指令,被重写为 /bbb.html ,然后匹配到 location /bbb.html {} ,接着执行 location 级的 rewrite 指令,先重写为 /ccc.html ,再重写为 /ddd.html ;由于 URI 被 location 级的 rewrite 指令重写了,因此需要重新进行 location 的匹配,相当于重写后的 URI 被当做一个新的请求,会重新执行 server 级的 rewrite ,然后重新匹配 location ,日志“ 2011/08/08 10:05:41 [notice] 31592#0: *90 "^/aaa\.html$" does not match "/ddd.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090" ”体现了重新匹配 location 的流程。

例题 2

配置:
error_log  logs/error.log info;
server {
listen       9090;
server_name  localhost;
root html;
rewrite_log on;
rewrite "^/aaa\.html$"  /bbb.html;
rewrite "^/ccc\.html$"  /ddd.html;
location  /bbb.html {
rewrite "^/bbb\.html$" /ccc.html;
}  
location  /ddd.html {
rewrite "^/ddd\.html$" /eee.html;
}
}   
结果:
[root@web108 ~]# curl http://localhost:9090/aaa.html
eee html file
[root@web108 ~]#
日志:
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/aaa\.html$" matches "/aaa.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 rewritten data: "/bbb.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/ccc\.html$" does not match "/bbb.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/bbb\.html$" matches "/bbb.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 rewritten data: "/ccc.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/aaa\.html$" does not match "/ccc.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/ccc\.html$" matches "/ccc.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 rewritten data: "/ddd.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/ddd\.html$" matches "/ddd.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 rewritten data: "/eee.html" , args: "", client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/aaa\.html$" does not match "/eee.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/ccc\.html$" does not match "/eee.html" , client: 127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [info] 2218#0: *91 client 127.0.0.1 closed keepalive connection
解释:
第一次迭代 location 匹配
GET /aaa.html ,首先执行 server 级的重写,“ rewrite "^/aaa\.html$"  /bbb.html ”把 /aaa.html 重写为 /bbb.html ,但 /bbb.html 没匹配上“ rewrite "^/ccc\.html$"  /ddd.html ”,最终保留 /bbb.html ;接着,匹配 location /bbb.html {} ,执行 location 级的 rewrite 指令,把 /bbb.html 重写为 /ccc.html ,由于 URI 被 location 级 rewrite 重写,因此需要重新迭代 location 匹配。
第二次迭代 location 匹配
对于第一次迭代结果 /ccc.html ,首先依然是执行 server 级的 rewrite 指令,“ rewrite "^/aaa\.html$"  /bbb.html; ”跟 /ccc.html 不匹配,但“ rewrite "^/ccc\.html$"  /ddd.html; ”把 /ccc.html 重写为 /ddd.html ; server 级 rewrite 执行完后,接着 location 匹配, /ddd.html 匹配到 location /ddd.html {} ,执行 location 级的 rewrite 指令,把 /ddd.html 重写为 /eee.html 。同样由于 URI 被 location 级的 rewrite 指令重写,于是需要重新迭代 location 匹配。
第三次迭代 location 匹配
对于第二次迭代结果 /eee.html ,首先依然执行 server 级的 rewrite 指令,“ rewrite "^/aaa\.html$"  /bbb.html; ”和“ rewrite "^/ccc\.html$"  /ddd.html; ”,只不过它们都没匹配上 /eee.html ,接着 /eee.html 进行 location 匹配,也没有,最终结果是 /eee.html ,返回“ eee html file ”页面。
最后说明下,如果把上述配置修改成server级rewrite和location的编辑顺序调整:
server {
        listen       9090;
        server_name  localhost;
        root html;
        rewrite_log on;

location  /bbb.html {
            rewrite "^/bbb\.html$" /ccc.html;
        }  
        location  /ddd.html {
            rewrite "^/ddd\.html$" /eee.html;
        }

rewrite "^/aaa\.html$"  /bbb.html;
        rewrite "^/ccc\.html$"  /ddd.html;
}
结果是不会受影响的,也就是说location匹配迭代总是先执行server级rewrite,再进行location匹配,再执行location级的rewrite,如果URI因location级rewrite指令重写,则需要进行下一次迭代。但总的迭代次数不超过10次,否则nginx报500错误。
简单伪代码描述下rewrite执行过程:
boolean match_finish = false;
int match_count = 0;
while(!match_finish && match_count < 10) {
        match_count ++;
    (1)按编辑顺序执行server级的rewrite指令;
    (2)按重写后的URI匹配location;
    (3)
        String uri_before_location = uri;
        按编辑顺序执行location级的rewrite指令;
        String uri_after_location = rewrite(uri);
        if(uri_before_location != uri_after_location) {
            match_finish = false;           
        } else {
            match_finish = true;
        }
        if(location rewrite has last flag) {
            continue;//表示不执行后面的rewrite,直接进入下一次迭代
        }
        if(location rewrite has break flag) {
            break;//表示不执行后面的rewrite,并退出循环迭代
        }
}
if(match_count <= 10) {
    return HTTP_200;
} else {
    return HTTP_500;
}
转载地址:http://eyesmore.iteye.com/blog/1142162

转载于:https://blog.51cto.com/ourlinux/850475

Nginx 关于 Rewrite 执行顺序详解相关推荐

  1. unity 继承会调用start吗_Unity 继承MonoBehaviour脚本 执行顺序 详解

    先看结果 Awake ->OnEnable-> Start ->-> FixedUpdate-> Update  -> LateUpdate ->OnGUI ...

  2. Nginx中rewrite的用法详解

    文章目录 一.rewrite简介 1.1 rewrite跳转场景 1.2 rewrite跳转实现过程 1.3 rewrite实际场景 1.4 nginx正则表达式 二.rewrite命令 2.1 re ...

  3. 【异步系列二】Promise原理及执行顺序详解

    前言 Promise 是 javascript 中非常重要的一环,熟悉它是必须的,而且在面试中也常常会问到相关面试题. 在了解 Promise 之前,需要了解什么是异步编程,可以参考我的一篇文章:Ja ...

  4. java try、catch、finally及finally执行顺序详解

    1.为什么要用finally 先看一个没有finally的异常处理try-catch语句: 假设count为要使用到的资源,并且用完要求释放此资源.那么我们可以把释放资源的语句放到try-catch后 ...

  5. Python装饰器执行顺序详解

    探究多个装饰器执行顺序 装饰器是Python用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行顺序的一个迷思. 疑问 大部分涉及多个装饰器装饰的函数调用顺序时都会 ...

  6. C++三大继承构造函数的执行顺序详解

    写的挺好的,关于继承和构造函数的先后顺序问题. 转自: http://blog.csdn.net/daheiantian/archive/2011/02/18/6438782.aspx 一.单继承 核 ...

  7. javascript运行机制之执行顺序详解

    JavaScript是一种描述型脚本语言,它不同于java或C#等编译性语言,它不需要进行编译成中间语言,而是由浏览器进行动态地解析与执行.那么JavaScript是怎么来进行解析的吗?它的执行顺序又 ...

  8. python两个装饰器执行顺序_python中多个装饰器的执行顺序详解

    装饰器是程序开发中经常会用到的一个功能,也是python语言开发的基础知识,如果能够在程序中合理的使用装饰器,不仅可以提高开发效率,而且可以让写的代码看上去显的高大上^_^ 使用场景 可以用到装饰器的 ...

  9. python装饰器的顺序_python中多个装饰器的执行顺序详解

    装饰器是程序开发中经常会用到的一个功能,也是python语言开发的基础知识,如果能够在程序中合理的使用装饰器,不仅可以提高开发效率,而且可以让写的代码看上去显的高大上^_^ 使用场景 可以用到装饰器的 ...

  10. Java 中 finally 与 return 的执行顺序详解

    java方法是在栈幀中执行,栈幀是线程私有栈的单位,执行方法的线程会为每一个方法分配一小块栈空间来作为该方法执行时的内存空间,栈幀分为三个区域: 1 . 操作数栈,用来保存正在执行的表达式中的操作数, ...

最新文章

  1. SAP WM初阶之TO报表LX10 - Evaluation of movements per storage type
  2. 在String.Format [复制]中转义大括号'{'
  3. 某office前台任意文件上传漏洞分析
  4. JavaScript——易班优课YOOC课群在线测试自动答题解决方案(十二)脚本整合
  5. ES中搜索结果各属性说明介绍,以及搜索中的timeout机制讲解(来自学习资料,34节)
  6. Aspx页面转静态页面
  7. 孙丕恕离开浪潮 仪器厂历时60年成为服务器龙头企业
  8. .Net Core开发日志——Global Tools
  9. layer之jquery 弹窗插件 (最后版本v1.8.5)
  10. 软件测试:职场上那些你不得不学会的事儿
  11. checkbox属性checked=checked已有,但却不显示打勾的解决办法
  12. kafka开启kerberos,报错server not found in kerberos database
  13. HCNA每日一练错误
  14. 侦探小明:如何判断电脑里突然出现的流氓广告来自哪个软件?
  15. go浏览器java_GO浏览器JAVAWP版下载_GO浏览器JAVA2016最新版_GO浏览器JAVA1.2.2-华军软件园...
  16. 双网络安全nvr/布控球,可双向同时接入国网B接口视频监控平台和国标28181平台
  17. BZOJ3161 : 孤舟蓑笠翁
  18. 深度学习论文-DAnetExFuseDFN
  19. 云服务器和虚拟主机有哪些区别
  20. FITC-GSL I荧光素标记的西非单叶豆凝集素 I

热门文章

  1. 课设——八皇后问题(N皇后解决)
  2. 伽卡他卡终极毁灭版---如何卸载!!!
  3. No suitable resolver
  4. ArcToolbox工具名英汉对应
  5. laravel安装barryvdh/laravel-snappy 笔记
  6. ​社交产品盈利方式产品模式设计案例
  7. 计算机应用能力考试ppt2003,全国专业技术人员计算机应用能力考试_PPT_2003_题库版.docx...
  8. 从区块链到DAG(三)--DAG共识之SPECTRE协议
  9. 棣拓DTAS公差分析软件-蒙特卡洛法公差分析软件-容差分析软件
  10. royal tsx连接闪退_Mac上使用Royal TSX链接服务器