你真的了解 Nginx rewrite 么?

本篇文章介绍了Nginx rewrite语法、逻辑,借助几个示例加强对于原理的理解。

回顾上篇文章:YARN 资源调度那些事儿

重定向

网站在使用 Nginx 时都会进行个性化配置满足自己的业务需要,而 URL 重写几乎是每个网站都必做的事情,Nginx 的 URL 重写规则不像 Apache 那样简单直接,逻辑相对要复杂一些,本文将通过例子的方式帮助大家理解 Nginx rewrite 原理,希望能对您有些启发。

Nginx 中重定向有多种方式:

外部重定向

return 指令返回 301 或 302(return 也可以返回其他状态码),可以放在 server 或 location 块中。例如:

还可以使用 rewrite 指令,例如:

内部重定向

return + error_page 指令的组合,或 try_files 指令和 rewrite 指令,非常灵活。

本文主要讲解 rewrite 的工作原理,其他指令的使用方法大家可以自行查阅 Nginx 官网。在使用 Nginx 的 rewrite 指令时,flag 可以设置为 last 和 break,这两个 flag 很容易混淆,后面我们会比较这两个 flag 的区别,下面通过示例我们来认识一下 rewrite 指令。

rewrite 语法

regex: 对请求的 URI 做正则匹配

replacement:目标 uri 匹配成功后替换的 url

可以使用的 flag 有以下 4 个(flag 也可以为空):

redirect:返回 302 临时重定向,客户端地址栏会显示跳转后的地址;

permanent:返回 301 永久重定向,客户端地址栏会显示跳转后的地址;

last:内部重定向,停止处理后续 rewrite 模块中的指令(客户端无感知);

break:内部重定向,停止处理后续 rewrite 模块中的指令(客户端无感知)。

NOTE:

1. regex 匹配的是 uri,不包含 hostname 和 query string,默认 query string 是被追加到 replacement 末尾,如果不希望在末尾追加请求的 query string,可以在 replacement 的末尾加一个 "?"。

2. 如果 replacement 是以 "http://","https://" 或 $scheme" 开始的字符串,那么 rewrite 指令停止后面的处理,直接返回给客户端,没有指定 flag 时,与 redirect 效果相同。

3. 没有 flag 的 rewrite 指令根据出现的顺序执行,flag 可以控制指令的执行顺序。

4. 在配置中开启 rewrite_log 指令,日志文件中会记录 rewrite 的匹配过程,有助于调试 rewrite 问题。

Nginx 请求处理流程

在讲 rewrite 前我们先来简单了解下 Nginx 请求处理流程,为什么需要了解请求处理流程呢?因为 rewrite 操作与其中几个 phase 关系很密切,熟悉了请求处理流程,理解 rewrite 执行逻辑就会很容易。在 Nginx 内部将请求处理划分为 11 个 phase,每个 phase 会执行对应的 handler,这里我们不打算逐个进行讲解。在 11 个 phase 中与 rewrite 指令逻辑有关的只有 4 个,所以在本文我们主要关注 SERVER_REWRITE、FIND_CONFIG、REWRITE 和 POST_REWRITE 这四个 phase。

首先我们要清楚的是:

server 块中的 rewrite 模块指令在 SERVER_REWRITE 阶段解析;

location 块中的 rewrite 模块指令在 REWRITE 阶段解析;

SERVER_REWRITE - 请求到达后首先处理这个阶段的 rewrite 指令操作

FIND_CONFIG - 根据 SERVER_REWRITE 阶段得到的 uri 查找 location

REWRITE - 确定 location 后执行 locaton 中 rewrite 操作

POST_REWRITE - 根据上一阶段的 uri 重写结果做决策,可能跳回 FIND_CONFIG 阶段重新查找 location,也可能继续执行后边的 phase。例如:在 location 中配置了 rewrite 指令并且指定 flag=break,执行完本条 rewrite 终止后边的 rewrite 匹配,然后执行 PREACCESS 阶段中的 handler。同样的场景下 flag=last,执行完本条 rewrite 终止后边的 rewrite 匹配,然后跳到 FIND_CONFIG 阶段再次查找 location。未指定 flag 的情况与 flag=last 类似,唯一区别是在同一层级中未指定 flag 的 rewrite 语句不会终止后续的 rewrite 匹配。

通过例子理解 rewrite 指令

1

未指定 flag

未指定 flag 的 rewrite 会按照出现顺序进行匹配,server 块中 rewrite 匹配完以后根据改写的 uri 查找 location,然后再匹配 location 中的 rewrite,location 中的 rewrite 指令匹配成功后会再次查找 location。

说明:

匹配第一条 rewrite 成功,uri 被改写为 / mi_one/mi_zero/hello,没有指定 flag 的 rewrite 继续匹配后面的 rewrite

匹配第二条 rewrite 成功,此时 uri 被改写为 / mi_two/mi_zero/hello

查找 location,mi_two 被确定为最终的 location

说明:

匹配 server 块中的 rewrite 成功,uri 被改写为 / mi_one/hello

server 块中只有一条 rewrite 指令,开始查找 location

location mi_one 被找到,开始匹配 location 中 rewrite

location 中的 rewrite 匹配成功,uri 被改写为 / mi_two/hello

再次查找 location,mi_two 被确定为最终使用的 location

再来看一个例子:

说明:

匹配第一条 rewrite 成功,由于 replacement 是以 http:// 开始的字符串,所以 rewrite 指令直接返回给客户端 302,并且停止匹配后续的 rewrite。

2

flag 指定为 redirect

指定 flag 为 redirect 时,rewrite 匹配成功后直接返回给客户端 302,不会继续匹配后续的 rewrite。

再来看一个例子:

3

flag 指定为 permanent

指定 flag=permanent 时,与 redirect 效果相同,唯一的区别 http code 返回 301。

4

flag 指定为 last

rewrite 的 last 和 break 这两个 flag 使用场景很多并且也很容易混淆,他们的共同点都会停止当前层级后续的 rewrite 匹配,区别需要分两种情况:第一种使用在 server block 中,last 和 break 没有区别。第二种使用在 location block 中,last 会根据改写的 uri 重新查找 location,break 不会重新查找 location,而是在当前 location 中执行后续的指令。(注:last 和 break 不仅停止 rewrite 的匹配,同时还会停止 Nginx rewrite 模块中其他指令的执行,例如:set、return 指令)

说明:

匹配第一条 rewrite 成功,uri 被改写为 / mi_two/hello,由于 flag 指定为 last 会停止后续的 rewrite 的匹配(仅停止 server block 中的 rewrite 匹配),所以会根据改写的 uri 查找 location

再来看一个 last 在 location block 中使用的例子:

说明:

匹配 server block 中第一条 rewrite 成功,uri 被改写为 / mi_one/hello

查找 location,mi_one 被确定为使用的 location

匹配 location block 中的 rewrite,location 中的第一条 rewrite 匹配成功,uri 被改写为 / mi_three/mi_one/hello,由于 flag 指定为 last,所以停止 location 中后续的 rewrite 匹配,此时再根据 uri=/mi_three/mi_one/hello 查找 location,最终 mi_three 被确定为使用的 location

5

flag 指定为 break

说明:

匹配 server block 中第一条 rewrite 成功,uri 被改写为 / mi_two/hello,由于 flag 指定为 break 所以会停止 server block 中后续的 rewrite 匹配,根据 uri=/mi_two/hello 查找 location,最终 mi_two 被确定为使用的 location

说明:

匹配 server block 中第一条 rewrite 成功,uri 被改写为 / mi_one/hello

根据 uri=/mi_one/hello 查找 location,mi_one 被确定为使用的 location

匹配 location block 中的 rewrite,location 中的第一条 rewrite 匹配成功,uri 被改写为 / mi_three/mi_one/hello,由于 flag 指定为 break,所以停止 location 中后续的 rewrite 匹配,并且把当前 location 作为最终使用的 location,不会重新查找 location(last 会继续查找 location)

总结

在 Nginx 的配置中可以实现简单的编程,理解起来相对有点难度,通过阅读此文希望能对你有些启发,能够根据项目需求可以配置更复杂的 rewrite 规则。想要更深入的理解 rewrite,还需要大家自己动手实践。

FYI

https://github.com/agile6v/awesome-nginx

http://nginx.org/en/docs/http/ngx_http_rewrite_module.html

https://www.nginx.com/blog/creating-nginx-rewrite-rules/

https://www.nginx.com/blog/converting-apache-to-nginx-rewrite-rules

http://www.thegeekstuff.com/2017/08/nginx-rewrite-examples/

http://winginx.com/en/htaccess

https://w3techs.com/technologies/overview/web_server/all

http://nginx.org/en/docs/dev/development_guide.html#httpphases

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180130G0UUVV00?refer=cp_1026

扫码关注云+社区