rewrite模块(ngx_http_rewrite_module)
nginx通过ngx_http_rewrite_module模块支持url重写、支持if条件判断,但不支持else。另外该模块需要PCRE支持,应在编译nginx时指定PCRE支持。
rewrite是实现URL重写的关键指令,根据regex(正则表达式)部分内容,重定向到replacement,结尾是flag标记。
在rewrite中,如果使用小括号(),那么在小括号之间匹配的内容,可以在后面通过$1来引用,$2表示的是前面第二个()里的内容。
rewrite语法格式及参数语法说明如下:
rewrite [flag]; 关键字 正则 替代内容 flag标记 关键字:其中关键字error_log不能改变 正则:perl兼容正则表达式语句进行规则匹配 替代内容:将正则匹配的内容替换成replacement flag标记:rewrite支持的flag标记 flag标记说明: last #本条规则匹配完成后,继续向下匹配新的location URI规则 break #本条规则匹配完成即终止,不再匹配后面的任何规则 redirect #返回302临时重定向,浏览器地址会显示跳转后的URL地址 permanent #返回301永久重定向,浏览器地址栏会显示跳转后的URL地址 |
regex 常用正则表达式说明
字符 | 描述 |
\ | 将后面接着的字符标记为一个特殊字符或一个原义字符或一个向后引用。如“\n”匹配一个换行符,而“\$”则匹配“$” |
^ | 匹配输入字符串的起始位置 |
$ | 匹配输入字符串的结束位置 |
* | 匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll” |
+ | 匹配前面的字符一次或多次。如“ol+”能匹配“ol”及“oll”、“oll”,但不能匹配“o” |
? | 匹配前面的字符零次或一次,例如“do(es)?”能匹配“do”或者“does”,"?"等效于"{0,1}" |
. | 匹配除“\n”之外的任何单个字符,若要匹配包括“\n”在内的任意字符,请使用诸如“[.\n]”之类的模式。 |
(pattern) | 匹配括号内pattern并可以在后面获取对应的匹配,常用$0...$9属性获取小括号中的匹配内容,要匹配圆括号字符需要\(Content\) |
Rewrite模块指令
1)break
1 2 3 | Syntax: break; Default:— Context:server, location, if |
此指令的意思是停止执行当前虚拟主机的后续rewrite指令集。使用示例如下:
1 2 3 4 | if ($slow) { limit_rate 10k; break; } |
2)if
1 2 3 | Syntax: if (condition) { ... } Default:— Context:server, location |
对给定的条件(condition)进行判断,如果条件为真,大括号内的rewrite指令将被执行。
条件(conditon)可以是如下任何操作:
1. 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false;
2. 使用“=”和“!=”比较一个变量和字符串;
3. 使用“~”做正则表达式匹配,“~*”做不区分大小写的正则匹配,“!~”做区分大小写的正则不匹配;
4. 使用“-f”和“!-f” 检查一个文件是否存在;
5. 使用“-d”和“!-d”检查一个目录是否存在;
6. 使用“-e”和“!-e”检查一个文件、目录、符号链接是否存在;
7. 使用“-x”和“ !-x”检查一个文件是否可执行;
全局变量
下面是可以用作if判断的全局变量
1 | $args #这个变量等于请求行中的参数,同$query_string; |
2 | $content_length #请求头中的Content-length字段; |
3 | $content_type #请求头中的Content-Type字段; |
4 | $document_root #当前请求在root指令中指定的值,如:root /var/www/html; |
5 | $host #请求主机头字段,否则为服务器名称; |
6 | $http_user_agent #客户端agent信息; |
7 | $http_cookie #客户端cookie信息; |
8 | $limit_rate #这个变量可以限制连接速率; |
9 | $request_method #客户端请求的动作,通常为GET或POST; |
10 | $remote_addr #客户端的IP地址; |
11 | $remote_port #客户端的端口; |
12 | $remote_user #已经经过Auth Basic Module验证的用户名; |
13 | $request_filename #当前请求的文件路径,由root或alias指令与URI请求生成; |
14 | $scheme #HTTP方法(如http,https); |
15 | $server_protocol #请求使用的协议,通常是HTTP/1.0或HTTP/1.1; |
16 | $server_addr #服务器地址,在完成一次系统调用后可以确定这个值; |
17 | $server_name #服务器名称; |
18 | $server_port #请求到达服务器的端口号; |
19 | $request_uri #包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”; |
20 | $uri #不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”; |
21 | $document_uri #与$uri相同,例:http://localhost:88/test1/test2/test.php; |
例如:http://localhost:88/test1/test2/test.php这个URL,其中:
1 | $host:localhost |
2 | $server_port:88 |
3 | $request_uri:http://localhost:88/test1/test2/test.php |
4 | $document_uri:/test1/test2/test.php |
5 | $document_root:/var/www/html |
6 | $request_filename:/var/www/html/test1/test2/test.php |
3)return
1 2 3 4 5 | Syntax: return code [text]; return code URL; return URL; Default:— Context:server, location, if |
停止处理并为客户端返回状态码,非标准的444状态码将关闭连接,不发送任何响应头。可以使用的状态码有:204,400,402-406,408,410, 411, 413, 416与500-504。如果状态码附带文字段落,该文本将被放置在响应主体。相反,如果状态码后面是一个URL,该URL将成为location头部值。没有状态码的URL将被视为一个302状态码。
4)rewrite
1 2 3 | Syntax: rewrite regex replacement [flag]; Default:— Context:server, location, if |
rewrite指令的功能就是,使用nginx提供的全局变量或自己设置的变量,然后结合正则表达式和标志位实现url重写以及重定向。rewrite指令只能放在server、location或if中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如 http://ywnds.com/a/we/index.php?id=1&u=str,只对/a/we/index.php重写,语法如上面所示。
如果想对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。
或许看rewrite和location功能有点像,都能实现跳转,主要区别在于rewrite是在同一域名内更改获取资源的路径,而location是对一类路径做控制访问或反向代理,可以proxy_pass到其他机器。很多情况下rewrite也会写在location里,它们的执行顺序是:
1)处理在server级别中定义的模块指令;
2)为请求查找location;
3)处理在选中的location中定义的模块指令,如果指令改变了URI,按新的URI查找location。这个循环至多重复10次,之后nginx返回错误500 (Internal Server Error);
如果一个URI匹配了rewrite指令指定的正则表达式(regex),则URI就按照replacement进行重写,而rewrite按配置文件中出现的顺序执行。其中flag标志可以停止继续处理。如果replacement以”http://”或”https://”开始,将不再继续处理,那么这个重定向将直接返回给客户端。
flag可以是如下参数:
- last,完成该rewrite规则的执行后,停止处理后续rewrite指令集;然后查找匹配改变后URI的新location;
- break,完成该rewrite规则的执行后,停止处理后续rewrite指令集,并不再重新查找;但是当前location内剩余非rewrite语句和location外的的非rewrite语句可以执行;
- redirect,返回302临时重定向,地址栏会显示跳转后的地址;
- permanent,返回301永久重定向,地址栏会显示跳转后的地址;即表示如果客户端不清理浏览器缓存,那么返回的结果将永久保存在客户端浏览器中了。
因为301和302不能简单的只返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。这里last和break区别有点难以理解:
1)last一般写在server和if中,而break一般使用在location中;
2)last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配;
3)break和last都能组织继续执行后面的rewrite指令。
示例:
1 2 3 4 5 6 7 | server { ... rewrite ^(/download/.*)/media/(.*)..*$ $1/mp3/$2.mp3 last; rewrite ^(/download/.*)/audio/(.*)..*$ $1/mp3/$2.ra last; return 403; ... } |
如果这些rewrite放到“/download/”路径,那么location如下所示,这时应使用break而不是last,使用last将循环10次匹配,然后返回500错误:
1 2 3 4 5 | location /download/ { rewrite ^(/download/.*)/media/(.*)..*$ $1/mp3/$2.mp3 break; rewrite ^(/download/.*)/audio/(.*)..*$ $1/mp3/$2.ra break; return 403; } |
对于重写后的URL(replacement)包含原请求的请求参数,原URL的?后的内容。如果不想带原请求的参数,可以在replacement后加一个问号。如下,我们加了一个自定义的参数user=$1,然后在结尾处放了一个问号?,把原请求的参数去掉。
1 | rewrite ^/users/(.*)$ /show?user=$1? last; |
注: 对花括号“}”或“;”来说,使用时需要用双引号或单引号包围。因为他们既能用在重定向的正则表达式里,也是用在配置文件里分割代码块, 为了避免冲突, 正则表达式里带花括号的话,应该用双引号(或者单引号)包围。比如,要将类似以下的URI:”/photos/123456″重定向到”/path/to/photos/12/1234/123456.png”可以用以下方法 (注意双引号):
1 | rewrite "/photos/([0-9] {2})([0-9] {2})([0-9] {2})" /path/to/photos" |
5)set
1 2 3 | Syntax: set $variable value; Default:— Context:server, location, if |
定义一个变量并赋值,值可以是文本,变量或者文本变量混合体。
6)rewrite_log
1 2 3 | Syntax: rewrite_log on | off; Default:rewrite_log off; Context:http, server, location, if |
开启或关闭以notice级别打印rewrite处理日志到error log文件。nginx打开rewrite log的例子如下:
1 2 | rewrite_log on; //打开rewrite log error_log logs/xxx.error.log notice; //把error log的级别调整为notice |
7)uninitialized_variable_warn
1 2 3 | Syntax: uninitialized_variable_warn on | off; Default:uninitialized_variable_warn on; Context:http, server, location, if |
控制是否输出为初始化的变量到日志。
重写规则组成部分
第一部分:任何重写规则的第一部分都是一个正则表达式
正则表达式可以使用括号来捕获,后续可以根据位置来将其引用,位置变量值取决于捕获正则表达式中的顺序,$1引用第一个括号中的值,$2引用第二个括号中的值,以此类推。如:
1 | ^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$ |
$1是两个小写字母组成的字符串,$2是由小写字母和0到9的数字组成的5个字符的字符串,$3将是个文件名,$4是png、jpg、gif中的其中一个。
第二部分:重写规则的第二部分是URI
当重写规则第一部分被匹配到了之后,则请求被改写,那么该URI可能包含正则表达式中的捕获的位置参数或这个级别下的nginx任何配置变量。如:
1 | /data?file=$3.$4 |
如果这个URI不匹配nginx配置的任何location,那么将给客户端返回301(永久重定向)或302(临时重定向)的状态码来表示重定向类型。该状态码可以通过第三个参数来明确指定。
第三部分:重写规则的第三部分就是标记(flag)
第三部分也就是尾部的标记(flag), last标记将导致重写后的URI搜索匹配nginx的其他location,最多可循环10次。如:
1 | rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4 last; |