nginx: http_rewrite_module

陪她去流浪 桃子 2015年04月17日 编辑 阅读次数:3088

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.161.0.13 (为啥有两个?)之前,状态码307都不被作为重定向看待。

同时还可以去看看 error_page 指令。

语法rewrite regex replacement [flag];
默认 
环境server, location, if

如果指定的正则表达式“regex”匹配一个请求URI,则用替换字符串“replacement”替换该URI(PCRE的替换,支持变量捕获)。

rewrite 指令按照在配置文件中出现的顺序被顺序地执行。如果替换字符串以“http://”或“https://”开始,当前请求的处理将立即停止,并向客户端返回重定向。

还可以通过 flag 终止后续处理,可选的 flag 参数可以是以下之一:

last
停止处理 ngx_http_rewrite_module 模块的当前指令集,并搜索匹配当前 URL 的新的 location
break
停止处理 ngx_http_rewrite_module 模块的当前指令集,就像 break 指令那样。
redirect
返回302临时重定向。用于当替换字符串不是以“http://”或“https://”开始时。
permanent
返回301永久重定向。

完整的重定向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 中。

语法set $variable $value;
默认 
环境server, location, if

把变量 $variable 设置为值 $value

值可以包含文本、变量、或前两者的结合。

语法uninitialized_variable_warn on | off;
默认uninitialized_variable_warn on;
环境http, server, location, if

控制是否记录对于未初始化的变量的警告到日志。

内部实现

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

标签:nginx