nginx: http_rewrite_module
ngx_http_rewrite_module 模块通过 使用正则表达式、返回重定向、基于条件的配置 来改变请求URI。
ngx_http_rewrite_module 模块指令按照下面的顺序被处理:
- 顺序执行:指定于 server 级的该模块的指令按顺序被执行。
- 重复执行:
- 基于某请求URI搜索location;
- 位于找到的location中的该模块的指令按顺序被执行;
- 如果某请求URI被重写(rewritten),该循环会被重复执行,但不会超过10次。
指令
语法 | break; |
默认 | |
环境 | server, location, if |
停止处理 ngx_http_rewrite_module模块 的当前指令集。
如果某指令位于location内,那么该请求的后续处理将继续在该location中处理。
示例:
if ($slow) { limit_rate 10k; break; }
语法 | if (condition) { ... } |
默认 | |
环境 | server, location |
注:if就是一个普通的指令,并不像真正的程序设计语言中的if,所以应该像所有nginx的指令一样:指令和参数之间都至少要有一个空白字符,否则就是错误的。
将评估条件 condition,如果为真,花括号内的指令将被执行(这些指令被分配给当前请求处理)。if指令内的配置由先前配置级别继承而来。
条件(condition)可以是以下几种:
- 变量名;
如果该变量的值为空串或“0”则条件为假。
注:在版本1.0.1之前,任何以“0”开始的字符串被考虑为假。 - 相等性判断;
用等于“=”和不等于“!=”比较一个变量和一个字符串。
- 正则表达式变量匹配;
可用运算符:“~”(区分大小写)、“~*”(不区分大小写)。正则表达式中可以包含捕获,后续的指令可以通过变量 $1..$9 来重复使用这些捕获值。如果一个正则表达式包含“}”或“;”字符,整个正则表达式应该放在一对单引号或双引号内,以避免被错误地解析。
- 判断文件(不判断目录)是否存在;
“-f”判断存在,“!-f”判断不存在。
- 判断目录(不判断文件)是否存在;
“-d”判断存在,“!-d”判断不存在。
- 判断文件、目录、符号链接是否存在;
“-e”判断存在,“!-e”判断不存在。
- 判断某文件是否是可执行文件;
“-e”判断可执行,“!-e”判断不可执行。
示例:
# 变量 if ($invalid_referer) { return 403; } # 变量 if ($slow) { limit_rate 10k; } # 相等性判断 if ($request_method = POST) { return 405; } # 正则表达式(区分大小写) if ($http_user_agent ~ MSIE) { rewrite ^(.*)$ /msie/$1 break; } # 正则表达式(不区分大小写) if ($http_cookie ~* "id=([^;]+)(?:;|$)") { set $id $1; } # 文件存在性 if (-f $uri) { # ... }
语法 |
return code [text]; return code URL; return URL; |
默认 | |
环境 | server, location, if |
停止当前请求的处理并向客户端返回指定的HTTP状态码(code)。非标准代码 444 将不发送任何响应头部就关闭与客户端的连接(在浏览器看来就是:连接被重置。表现就像访问Google那样(Connection was Reset))。
从版本 0.8.42 开始,可以为代码301(永久重定向,Permanent Moved)、302(临时重定向,Found)、303(See Other)、307(对于POST请求的临时重定向,Temporary Redirect)同时指定一个重定向到的URL,还可以为其它代码指定响应内容(text)。响应内容和重定向URL可以包含变量。
特殊情况:重定向URL可以是本服务器上的某URI,这种情况下,将按照当前请求的协议(scheme,$scheme,一般为 HTTP/HTTPS)、server_name_in_redirect指令、port_in_redirect指令组织最终的重定向URL。
另外:对于302的临时重定向,可以仅指定URL参数(不需要写代码302,第3种语法)。这种情况下,URL必须以“http://”、“https://”或“$scheme”开始。该URL同样可以包含变量。
注:在版本 0.7.51 之前,仅可以使用这些状态码:204, 400, 402 - 406, 408, 410, 411, 413, 416, 500-504。
注:在版本 1.1.16 和 1.0.13 (为啥有两个?)之前,状态码307都不被作为重定向看待。
同时还可以去看看 error_page 指令。
语法 | rewrite regex replacement [flag]; |
默认 | |
环境 | server, location, if |
如果指定的正则表达式“regex”匹配一个请求URI,则用替换字符串“replacement”替换该URI(PCRE的替换,支持变量捕获)。
rewrite 指令按照在配置文件中出现的顺序被顺序地执行。如果替换字符串以“http://”或“https://”开始,当前请求的处理将立即停止,并向客户端返回重定向。
还可以通过 flag 终止后续处理,可选的 flag 参数可以是以下之一:
完整的重定向URL将按照当前请求的协议(scheme,$scheme,一般为 HTTP/HTTPS)、server_name_in_redirect指令、port_in_redirect指令组织而成。
示例:
server { ... rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last; rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra last; return 403; ... }
但是,对于上面的例子,如果把这些指令被放在“/download/” location 中,last 应该用 break 替换,否则,nginx 将产生 10次 循环,并最终返回 500 内部服务器错误 错误。正确的应该是下面这样:
location /download/ { rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break; rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra break; return 403; }
如果替换字符串包含新的请求参数,原有的请求参数将追加到这些新的参数后面。如果不需要原有的参数,可以在替换字符串的最后加一个问号(?),就像下面这样:
rewrite ^/users/(.*)$ /show?user=$1? last;
如果一个正则表达式包含“}”或“;”字符,整个正则表达式应该放在一对单引号或双引号内,以避免被错误地解析。
语法 | rewrite_log on | off; |
默认 | rewrite_log off; |
环境 | http, server, location, if |
启用或禁用 ngx_http_rewrite_module 模块指令的日志记录。如果启用,这些指令的处理结果将以通知(notice)级别记录到 error_log 中。
内部实现
ngx_http_rewrite_module 模块指令在读取配置时被编译为在请求其间解释处理的内部指令。解释器是一个虚拟的栈机(virtual stack machine)。
比如,下面的指令
location /download/ { if ($forbidden) { return 403; } if ($slow) { limit_rate 10k; } rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break; }将被翻译(translate)成这些指令:
variable $forbidden check against zero return 403 end of code variable $slow check against zero match of regular expression copy "/" copy $1 copy "/mp3/" copy $2 copy ".mp3" end of regular expression end of code
注:因为与当前模块不相关,故没有列出指令 limit_rate。
一个单独的配置将会为 if 指令块创建,如果if的条件为真,该请求就会被分配这个单独的配置。
对于指令:
rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break;如果正则表达式中的第1个斜杠在括号内,就像下面这样:
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;则可以少一条内部指令:
match of regular expression copy $1 copy "/mp3/" copy $2 copy ".mp3" end of regular expression end of code