1.概述
- Rewrite主要的功能就是实现URL的重写。Nginx的Rewrite规则采用PCRE(Perl Compatible Regular Expressions) Perl 兼容正则表达式的语法进行规则匹配。
- 通过Rewrite规则,可以实现规范的 URL、根据变量来做URL转向及选择配置。例如,一些使用MVC框架的程序只有一个入口,可以通过Rewrite来实现。一些动态URL地址须要伪装成静态HTML,便于搜索引擎抓取,也需要 Rewrite来处理。一些由于目录结构、域名变化的旧URL,须要跳转到新的URL上,也可以通过Rewrite来处理
2.基础规则
-
Nginx Rewrite 规则相关指令有 if、rewrite、set、return、break 等,其中 rewrite 是最关键的指令。一-个简单的 Nginx Rewrite 规则语法如下:
rewrite ^/b/(.*)\.html /play.php?video=$l break;
如果加上f语句,示例如下:
if(!-f $request_filename) { rewrite ^/img/(.*)$ /site/$host/images/$1 last; }
2.1 break指令
语法: break
默认值: none
使用环境: server、location、if
该指令的作用是完成当前的规则集,不再处理rewrite 指令。示例如下:
if ($slow){
limit_rate 10k;
break;
}
2.2 if 指令
语法: if(condition){ ...}
默认值: none
使用环境: server、location
-
该指令用于检查一个条件是否符合,如果条件符合,则执行大括号内的语句。if 指令不支持嵌套,不支持多个条件
&&
和||
处理。
以下信息可以被指定为条件:-
变量名,错误的值包括: 空字符
""
,或者任何以0开始的字符串 -
变量比较可以使用
=
(表示等于)!=
(表示不等于) 运算符; -
正则表达式模式匹配可以使用
~*
和~
符号 -
~
符号表示区分大小写字母的匹配 -
~*
符号表示不区分大小写字母的匹配(例如 firefox 与 FireFox 是匹配的); -
!~
和!~*
符号的作用刚好和~
、!~*
相反,表示不匹配; -
-f
和!-f
用来文件是否存在 -
-d
和!-d
用来判断目录是否存在; -
-e
和!-e
用来断文件或目录是否存在 -
-x
和!-x
用来判断文件是否可执行。
-
部分正则表达式可以在圆括号()内,其值可以通过后面的变量$1至$9 访问
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$l break;
}
if ($http_cookie ~* "id=([^;] +)(?:;|$)”) {
set $id $l;
}
if ($request_method = POST ){
return 405;
}
if (!-f $request_filename) {
break;
proxy_pass http://127.0.0.1;
}
if ($slow) {
limit_rate 10k;
}
if ($invalid_referer){
return 403;
}
if ($args ^~ post=140){
rewrite ^ http://example.com/permanent;
}
2.3 return指令
语法: return code
默认值: none
使用环境:server、location、if
-
该指令用于结束规则的执行并返回状态码给客户端。状态码可以使用这值:
204、400、402406、408、410、411、413、416及500504。此外,非标准状态码 444 将以不发送任何 Header头的方式结束连接。示例,如果访问的URL以
Sh
和Bash
结尾,则返回状态码403:location ~ .*\.(sh|bash)?${ return 403; }
- 204 No Content
服务器成功处理了请求,但无须返回任何实体内容,并且希望返回更新了的元信息。响应可能通过实体头部的形式,返回新的或更新后的元信息。如果存在这些头部信息,则应当与所请求的变量相呼应。如果客户端是浏览器,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息,也应当被应用到用户浏览器活动视图中的文档。 - 400 Bad Request
由于包含语法错误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。 - 402 Payment Required
该状态码是为了将来可能的需求而预留的 - 403 Forbidden
服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个 HEAD 请求,而且服务器希望能够讲清楚为何请求不能被执行,就应该在实体内描述拒绝的原因。当然服务器也可以返回一个404 响应,假如它不希望让客户端获得任何信息。 - 404 Not Found
请求失败,请求所希望得到的资源未在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况,应当使用 410 状态码来告知旧资源因为某些内部的配置机制问题,已经永久地不可用,而且没有任何可以跳转的地址。404 这个状态码被广泛应用丁当服务器不想揭示为何请求被拒绝,或者没有其他适合的响应可用的情况下。 - 405 Method Not Allowed
请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个 Alow 头信息,用以表示出当前资源能够接受的请求方法的列表。
鉴于 PUT,DELETE 方法会对服务器上的资源进行写操作,因而绝大部分的网页服务器都不支持或在默认配置下不支持上述请求方法,对于此类请求均会返回 405 错误。 - 406 Not Acceptable
请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。除非这是一个 HEAD 请求,否则该响应就应当返回一个包含可以让用户或浏览器从中选择最合适的实体特性及地址列表的实体。实体的格式由 Content-Type 头中定义的媒体类型决定。 - 408 Request Timeout
请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。客户端可以随时再次提交这一请求而无须进行任何更改。 - 410 Gone
被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。如果可能,拥有链接编辑功能的客户端应当在获得用户许可后删除所有指向这个地址的引用。如果服务器不知道或无法确定这个状况是否是永久的,就应该使用 404 状态码除非额外说明,否则这个响应是可缓存的。
410 响应的目的主要是帮助网站管理员维护网站,通知用户该资源已经不再可用,并且服务器拥有者希望所有指向这个资源的远端连接也被删除。这类事件在限时、增值服务中很普遍。同样,410 响应也被用于通知客户端在当前服务器站点上,原本属于某个个人的资源已经不再可用当然,是否要把所有永久不可用的资源标记为410 Gone,以及是否要保持此标记多长时间,完全取决于服务器拥有者。 - 411 Length Required
服务器拒绝在没有定义 Content-Length 头的情况下接受请求。在添加了表明请求消息体长度的有效 Content-Length 头之后,客户端可以再次提交该请求。 - 413 Request Entity Too Large
服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或能够处理的范围。此种情况下,服务器可以关闭连接以免客户端继续发送此请求
如果这个状况是临时的,服务器应当返回一个 Retry-After 的响应头,以告知客户端可以在多少时间以后重新尝试。 - 416 Requested Range Not Satisfiable
如果请求中包含了 Range 请求头,并且 Range 中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义 if-Range 请求头,那么服务器就应当返回416状态码。
假如 Range 使用的是字节范围,那么这种情况就是指请求指定的所有数据范围的首字节位置都超过了当前资源的长度。服务器也应当在返回416 状态码的同时,包含一个 Content-Range实体头,用以指明当前资源的长度。这个响应也被禁止使用 multipart/byteranges 作为其Content-Type. - 500 Internal Server Error
服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。 - 501 Not Implemented
服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求时。 - 502 Bad Gateway
作为网关或代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应 - 503 Service Unavailable
由于临时的服务器维护或过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头用以标明这个延迟时间。如果没有给出这个 Retry-After 信息,那么客户端应当以处理500 响应的方式处理它。注意:503 状态码的存在并不意味着服务器在过载的时候必须使用它。某些服务器只不过是希望拒绝客户端的连接 - 504 Gateway Timeout
作为网关或代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTPFTP、LDAP)或辅助服务器(例如DNS)收到响应。
注意:某些代理服务器在DNS查询超时时会返回400或500错误。
- 204 No Content
2.4 rewrite指令
语法: rewrite regex replacement flag
默认值: none
使用环境: server、location、if
-
该指令根据表达式来重定向 URI,或者修改字符串。指令根据配置文件中的顺序来执行。
注意重写表达式只对相对路径有效。如果你想配对主机名,你应该使用if语句,代码如下:if($host ~* www\.(.*)){ set $host_without_www $1; rewrite ^(*)$ http://$host_without_wwwsl permanent; # $1 contains '/foo',not' www.mydomain.com/foo! }
-
如果替换串以http://开头,将会采用301或302跳转进行URL重定向。rewrite指令的最后一项参数为flag标记,支持的flag标记有:
- last 相当于Apache里的[L]标记,表示完成rewrite;
- break 本条规则匹配完成后,终止匹配,不再匹配后面的规则
- redirect 返回302临时重定向,浏览器地址栏会显示跳转后的URL地址
- permanent 返回301永久重定向,浏览器地址栏会显示跳转后的URL地址。
在以上的标记中,last和break 用来实现URI重写,浏览器地址栏的URL地址不变,但在服务器端访问的路径发生了变化。redirect和permanent用来实现URL跳转,浏览器地址栏会显示跳转后的URL地址。
last和break 标记的实现功能类似,但二者之间有细微的差别,使用alias 指令时必须用 last标记,使用proxy_pass指令时要使用break标记。last标记在本条rewrite规则执行完毕后,会对其所在的server{...}标签重新发起请求,而 break 标记则在本条规则匹配完成后,终止匹配,不再匹配后面的规则。例如以下这段规则,就必须使用 break 标记,使用 last 标记会导致死循环
location /cms/ {
proxy_pass http://testyourdomain.com;
rewrite “^/cms/(.*)\.html$" /cms/index.html break;
}
因此,一般在根 location 中或直接在 server 标签中编写 rewrite 规则推荐使用 last 标记,在非根 location 中,则使用 brea 标记。
rewrite ^(/download/.*)/media/(.*)\..*S $1/mp3/$2.mp3 last;
rewrite ^(/download/.*)/audio/(.*)\..*S $1/mp3/$2.ra last;
return403
location /download/ {
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra break;
}
如果被替换的 URI中含有参数 (即类似/app/test.php?id=5 之类的URI),默认情况下参数会被自动附加到替换串上,你可以通过在替换串的末尾加上?标记来解决这一问题。
rewrite ^/users/(.*)$ /show?user=$1? last;
不加 ?
标记和加上?
标记的 URL跳转区别:
rewrite ^/test(.*)$ http://www.yourdomain.com/home? permanent;
访问 http://www.yourdomain.com/test?id=5
经过301 跳转后的 URL地址为 http://www.yourdomain.com/home
注:对花括号({ 和})来说,它们既能用在重定向的正则表达式里,也能用在配置文件里分割代码块,为了避免冲突,正则表达式里如果带花括号,应该用双引号(或者单引号)包围。比如,要将类似以下的URL:
/photos/123456
重定向到:/path/to/photos/12/1234/123456.png
可以用以下方法 (注意双引号) :
rewrite "/photos/([0-9] {2}) ([0-9] {2})" /path/to/photos/$1/$1$2$3.png;
2.5 set指令
语法: set variable value
默认值: none
使用环境: server、location、if
- 该指令用于定义一个变量,并给变量赋值。变量的值可以为文本、变量及文本变量的联合。示例如下:
set $varname 'hello';
2.6 uninitialized_variable_warn 指令
语法:uninitialized_variable_warn on|off
默认值:uninitialized_variable_warn on
使用环境: http、server、location、if
- 该指令用于开启或关闭记录关于未初始化变量的警告信息,默认值为开启。
2.7 rewrite全局变量
$args
$content_length
Scontent_type
$document_root
$document_uri
Shost
$http_user_agent
$http_cookie
$limit_rate
$request_body_file
$request_method
$remote_addr
$remote_port
Sremote_user
$request_filename
$request_uri
$query_string
$scheme
$server_protocol
$server_addr
Sserver_name
Sserver_port
Suri
3 正则表达式语法
字符 | 描述 |
---|---|
\ | 将下一个字符标记为一个特殊字符,或一个原义字符,或一个向后引用,或一个八进制转义符。例如,\n 匹配一个换行符。序列\\ 匹配\ 而\( 则匹配( |
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配\n 或\r 之后的位置 |
$ | 匹配输入字符串的结束位置。如果设置了 RegExp 对象的 Multiline 属性,$也匹配\n 或\r 之前的位置 |
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配z 及Zoo 。* 等价于 |
+ | 匹配前面的子表达式一次或多次。例如,zo+ 能匹配zo 及zoo ,但不能匹配z 。+ 等价于 |
? | 匹配前面的子表达式零次或一次。例如,do(es)? 可以匹配do 或does 中的do 。?等价于{0,1) |
? | 当该字符紧跟在任何一个其他限制符(*,+,?,{n),{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串oooo ,o+? 将匹配单个o ,而o+ 将匹配所有o |
n是一个非负整数。匹配确定的n次。例如,o{2) 不能匹配Bob 中的o ,但是能匹配food 中的两个0 |
|
n是一个非负整数。至少匹配n次。例如,o{2,} ”不能匹配Bo 中的o ,但能匹配fooood 中的所有o 。o{1,} 等价于o+ 。o{0,) 则等价于o* |
|
m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,o{1,3} 将匹配“fooooood”中的前三个 o 。o{0,1} 等价于o? 。请注意在逗号和两个数之间不能有空格 |
|
. | 匹配除\n 之外的任何单个字符。要匹配包括\n 在内的任何字符,请使用像[.\n] 的模式 |
(pattern) | 匹配 patter 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在 VBScript中使用 SubMatches 集合,在JScript 中则使用S0...S9 属性。要匹配圆括号字符,请使用\( 或者\) |
(?:pattern) | 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或 字符`( |
?=pattern) | 正向预查,在任何匹配 patterm 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配无须获取供以后使用。例如`Windows(?=95 |
(?!pattern) | 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配无须获取供以后使用。例如`Windows(?!95 |
x|y | 匹配x 或y 。例如,`z |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如,[abc] 可以匹配plain 中的a |
[^xyz] | 负值字符集合。匹配未包含的任意字符。例如,[^abc] 可以匹配plain 中的p |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,[a-z] 可以匹配a 到2 范围内的任意小写字母字符 |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z] 可以匹配任何不在a 到z 范围内的任意字符 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如,er\b 可以匹配never 中的er ,但不能匹配verb 中的er |
\B | 匹配非单词边界。er\B 能匹配yerb 中的er ,但不能匹配never 中的er |
\cx | 匹配由x 指明的控制字符。例如,\cM 匹配一个Control-M 或回车符。x的值必须为 A-Z 或a-z 之一。否则,将c视为一个原义的c 字符 |
\d | 匹配一-个数字字符。等价于[0-9] |
\D | 匹配一个非数字字符。等价于[^0-9] |
\f | 匹配一个换页符。等价于\xOc 和\cL |
\n | 匹配一个换行符。等价于\xOa 和\cJ |
\r | 匹配一个回车符。等价于\xOd 和\cM |
\s | 匹配任何空白字符,包括空格、制表符、换页符等。等价于[\f\n\r\t\v] |
\S | 匹配任何非空白字符。等价于[^\f\n\r\t\v] |
\t | 匹配一个制表符。等价于\x09 和\cI |
\v | 匹配一个垂直制表符。等价于\xOb 和\cK |
\w | 匹配包括下划线的任何单词字符。等价于[A-Za-z0-9_] |
\W | 匹配任何非单词字符。等价于[^A-Za-z0-9_] |
\xn | 匹配 n, 其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,x41 匹配A 。x041 则等价于x04 &1 。正则表达式中可以使用 ASCI编码 |
\num | 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,(.)1 匹配两个连续的相同字符 |
\n | 标识一个八进制转义值或一个向后引用。如果\n 之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0~7),则n为一个八进制转义值 |
\nm | 标识一个八进制转义值或一个向后引用。如果\nm 之前至少有 nm 个获取的子表达式,则 nm为向后引用。如果\nm 之前至少有 n个获取的子表达式,则n为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若n和m 均为八进制数字(0~7),则nm 将匹配八进制转义值 nm |
\nml | 如果n为八进制数字(0~3),且m 和1均为八进制数字(0~7),则匹配八进制转义值 nml |
\un | 匹配 n,其中n是一个用4个十六进制数字表示的 Unicode 字符。例如,\u00A9 匹配版权符号(©) |