nginx rewrite执行顺序

本贴最后更新于 3366 天前,其中的信息可能已经时过境迁

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

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    141 引用 • 944 回帖
  • OneNote
    1 引用 • 3 回帖 • 2 关注
  • JetBrains

    JetBrains 是一家捷克的软件开发公司,该公司位于捷克的布拉格,并在俄国的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是 Java 编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA

    18 引用 • 54 回帖
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    168 引用 • 595 回帖
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    151 引用 • 257 回帖
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    90 引用 • 59 回帖 • 7 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    9553 引用 • 43501 回帖 • 101 关注
  • Access
    1 引用 • 3 回帖 • 4 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    6 引用 • 140 回帖 • 1 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    298 引用 • 762 回帖
  • 招聘

    哪里都缺人,哪里都不缺人。

    189 引用 • 1057 回帖 • 3 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 609 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 88 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    79 引用 • 431 回帖 • 1 关注
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    107 引用 • 127 回帖 • 348 关注
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 61 关注
  • PWA

    PWA(Progressive Web App)是 Google 在 2015 年提出、2016 年 6 月开始推广的项目。它结合了一系列现代 Web 技术,在网页应用中实现和原生应用相近的用户体验。

    14 引用 • 69 回帖 • 176 关注
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    78 引用 • 396 回帖 • 1 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 679 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖 • 1 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖
  • 强迫症

    强迫症(OCD)属于焦虑障碍的一种类型,是一组以强迫思维和强迫行为为主要临床表现的神经精神疾病,其特点为有意识的强迫和反强迫并存,一些毫无意义、甚至违背自己意愿的想法或冲动反反复复侵入患者的日常生活。

    15 引用 • 161 回帖