|
在新主机的迁移过程中,最大的困难就是WP permalink rewrite的设置.
因为旧主机是用的 Apache, 使用的是 WP 本身就可以更改的 .htaccess ,没有太大的难度.而这次在 VPS 上跑的是 Nginx,主要是因为 Nginx 的速度比 Apache 要快很多.
但是另一方面就不是那么舒服了,因为 Nginx 的 rewrite 跟 Apache 不同,而且是在服务器上面才能更改.
下面是其间的一些研究笔记.(以下用例如无特别说明均摘自nginx wiki)
一、Ngnix rewrite 基本语法
Nginx 的 rewrite 语法其实很简单.用到的指令无非是这几个
麻雀虽小,可御可萝五脏俱全.只是简单的几个指令却可以做出绝对不输apache的简单灵活的配置.
1.set
set主要是用来设置变量用的,没什么特别的。
2.if
if主要用来判断一些在rewrite语句中无法直接匹配的条件,比如检测文件存在与否,http header,cookie等。
用法:
- 当if表达式中的条件为true,则执行if块中的语句
- 当表达式只是一个变量时,如果值为空或者任何以 0 开头的字符串都会当作 false
- 直接比较内容时,使用 = 和 !=
- 使用正则表达式匹配时,使用
~ 大小写敏感匹配
~* 大小写不敏感匹配
!~ 大小写敏感不匹配
!~* 大小写不敏感不匹配
这几句话看起来有点绕,总之记住: ~为正则匹配, 后置 * 为大小写不敏感, 前置 ! 为”非”操作 。
随便一提,因为 nginx 使用花括号 {} 判断区块,所以当正则中包含花括号时,则必须用双引号将正则包起来.对下面讲到的 rewrite 语句中的正则亦是如此. 比如 “\d{4}\d{2}\.+”
- 使用-f,-d,-e,-x检测文件和目录:
-f 检测文件存在
-d 检测目录存在
-e 检测文件,目录或者符号链接存在
-x 检测文件可执行
跟~类似,前置!则为”非”操作 。
举例
1. 如果 UA 包含”MSIE”, rewrite 请求到 /msie 目录下
[PHP] 纯文本查看 复制代码 if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
}
2. 如果 cookie 匹配正则,设置变量 $id 等于正则引用部分
[PHP] 纯文本查看 复制代码 if ($http_cookie ~* "id=([^;] +)(?:;|$)" ) {
set $id $1;
}
3. 如果提交方法为POST,则返回状态405 (Method not allowed)
[PHP] 纯文本查看 复制代码 if ($request_method = POST ) {
return 405;
}
4. 如果请求文件名不存在, 则反向代理 localhost
[PHP] 纯文本查看 复制代码 if (!-f $request_filename) {
break;
proxy_pass [url]http://127.0.0.1[/url];
}
5. 如果query string中包含”post=140″,永久重定向到example.com
[PHP] 纯文本查看 复制代码 if ($args ~ post=140){
rewrite ^ [url]http://example.com/[/url] permanent;
}
return
return 可用来直接设置 HTTP 返回状态,比如 403,404 等 (301,302不可用 return 返回,这个下面会在 rewrite 提到)
break
立即停止 rewrite 检测,跟下面讲到的 rewrite 的 break flag 功能是一样的,区别在于前者是一个语句,后者是 rewrite 语句的 flag 。
rewrite
最核心的功能(废话)。
其中标志位有四种
break – 停止 rewrite 检测,也就是说当含有 break flag 的 rewrite 语句被执行时,该语句就是 rewrite 的最终结果
last – 停止 rewrite 检测,但是跟 break 有本质的不同,last 的语句不一定是最终结果,这点后面会跟 nginx 的 location 匹配一起提到
redirect – 返回 302 临时重定向,一般用于重定向到完整的URL(包含http:部分)
permanent – 返回 301 永久重定向,一般用于重定向到完整的URL(包含http:部分)
因为 301 和 302 不能简单的只单纯返回状态码,还必须有重定向的 URL, 这就是 return指令无法返回301,302 的原因了. 作为替换, rewrite 可以更灵活的使用 redirect 和 permanent 标志实现 301 和 302. 比如上一篇日志中提到的 Blog 搬家要做的域名重定向,在 nginx 中就会这么写:
[PHP] 纯文本查看 复制代码 rewrite ^(.*)$ [url]http://newdomain.com/[/url] permanent;
举例来说一下 rewrite 的实际应用:
[PHP] 纯文本查看 复制代码 rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
如果请求为 /download/eva/media/op1.mp3 则请求被rewrite到 /download/eva/mp3/op1.mp3
使用起来就是这样,很简单不是么? 不过要注意的是rewrite有很多潜规则需要注意
- rewrite 的生效区块为sever, location, if
- rewrite 只对相对路径进行匹配,不包含 hostname 比如说以上面 301 重定向的例子说明
[PHP] 纯文本查看 复制代码 rewrite ~* cafeneko\.info [url]http://newdomain.com/[/url] permanent;
这句是永远无法执行的,以这个 URL 为例:
http://blog.cafeneko.info/2010/10/neokoseseiki_in_new_home/?utm_source=rss&utm_medium=rss&utm_campaign=neokoseseiki_in_new_home
其中 cafeneko.info 叫做 hostname,再往后到 ? 为止叫做相对路径,? 后面的一串叫做 query string 。
对于 rewrite 来说,其正则表达式仅对 ”/2010/10/neokoseseiki_in_new_home” 这一部分进行匹配,即不包含hostname,也不包含query string 。所以除非相对路径中包含跟域名一样的 string, 否则是不会匹配的. 如果非要做域名匹配的话就要使用 if 语句了,比如进行去 www 跳转:
[PHP] 纯文本查看 复制代码 if ($host ~* ^www\.(cafeneko\.info)) {
set $host_without_www $1;
rewrite ^(.*)$ http://$host_without_www$1 permanent;
}
- 使用相对路径 rewrite 时,会根据 HTTP header 中的 HOST 跟 nginx 的 server_name 匹配后进行 rewrite,如果 HOST 不匹配或者没有 HOST 信息的话则 rewrite 到 server_name 设置的第一个域名,如果没有设置 server_name 的话,会使用本机的 localhost 进行 rewrite。
- 前面提到过, rewrite的正则是不匹配 query string 的,所以默认情况下,query string 是自动追加到 rewrite 后的地址上的,如果不想自动追加 query string ,则在 rewrite 地址的末尾添加?
[PHP] 纯文本查看 复制代码 rewrite ^/users/(.*)$ /show?user=$1? last;
rewrite的基本知识就是这么多..但还没有完..还有最头疼的部分没有说…
![]()
Nginx location 和 rewrite retry
nginx 的 rewrite 有个很奇特的特性 ---- rewrite后的 url 会再次进行 rewrite 检查,最多重试 10 次,10 次后还没有终止的话就会返回 HTTP 500
用过nginx的朋友都知道 location 区块, location 区块有点像 Apache 中的 RewriteBase, 但对于 nginx 来说 location 是控制的级别而已, 里面的内容不仅仅是rewrite.
这里必须稍微先讲一点 location 的知识。location 是 nginx 用来处理对同一个 server 不同的请求地址使用独立的配置的方式
举例:
[PHP] 纯文本查看 复制代码 location = / {
....配置A
}
location / {
....配置B
}
location ^~ /images/ {
....配置C
}
location ~* \.(gif|jpg|jpeg)$ {
....配置D
}
访问 / 会使用配置 A
访问 /documents/document.html 会使用配置 B
访问 /images/1.gif 会使用配置 C
访问 /documents/1.jpg 会使用配置 D
如何判断命中哪个 location 暂且按下不表, 我们在实战篇再回头来看这个问题。
现在我们只需要明白一个情况: nginx可以有多个 location 并使用不同的配。
sever 区块中如果有包含 rewrite 规则,则会最先执行,而且只会执行一次, 然后再判断命中哪个 location 的配置,再去执行该 location 中的 rewrite, 当该 location 中的 rewrite 执行完毕时, rewrite并不会停止,而是根据 rewrite 过的 URL 再次判断 location 并执行其中的配置. 那么,这里就存在一个问题,如果rewrite写的不正确的话,是会在location区块间造成无限循环的.所以 nginx 才会加一个最多重试10次的上限. 比如这个例子
[PHP] 纯文本查看 复制代码 location /download/ {
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
}
如果请求为 /download/eva/media/op1.mp3 则请求被 rewrite到 /download/eva/mp3/op1.mp3
结果 rewrite 的结果重新命中了 location /download/ 虽然这次并没有命中 rewrite 规则的正则表达式,但因为缺少终止 rewrite 的标志,其仍会不停重试 download 中 rewrite 规则直到达到10次上限返回 HTTP 500
认真的朋友这时就会问了,上面的 rewrite 规则不是有标志位 last 么? last 不是终止 rewrite 的意思么?
说到这里我就要抱怨下了,网上能找到关于nginx rewrite的文章中80%对last标志的解释都是:
……这他妈坑爹呢!!! 什么叫基本上都用? 什么是不基本的情况? =皿= 有兴趣的可以放狗”基本上都用这个Flag”…
我最终还是在stack overflow找到了答案:
last和break最大的不同在于
- break 是终止当前 location 的 rewrite 检测,而且不再进行 location 匹配
– last 是终止当前 location 的 rewrite 检测,但会继续重试 location 匹配并处理区块中的 rewrite 规则
还是这个该死的例子:
[PHP] 纯文本查看 复制代码 location /download/ {
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 ;
rewrite ^(/download/.*)/movie/(.*)\..*$ $1/avi/$2.mp3 ;
rewrite ^(/download/.*)/avvvv/(.*)\..*$ $1/rmvb/$2.mp3 ;
}
上面没有写标志位,请各位自行脑补…
如果请求为 /download/acg/moive/UBW.avi
last 的情况是: 在第 2 行 rewrite 处终止,并重试 location /download..死循环
break 的情况是: 在第2行rewrite处终止,其结果为最终的 rewrite 地址.
也就是说,上面的某位试图下载 eva op 不但没下到反而被 HTTP 500 射了一脸的例子正是因为用了 last 标志所以才会造成死循环,如果用 break 就没事了.
[PHP] 纯文本查看 复制代码 location /download/ {
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
}
对于这个问题,我个人的建议是,如果是全局性质的 rewrite,最好放在 server 区块中并减少不必要的 location 区块 。location 区块中的 rewrite 要想清楚是用 last 还是break。
有人可能会问,用break不就万无一失了么?
不对.有些情况是要用 last 的。典型的例子就是 wordpress 的 permalink rewrite
常见的情况下, wordpress 的 rewrite 是放在 location / 下面,并将请求 rewrite 到 /index.php
这时如果这里使用 break 乃就挂了,不信试试. b( ̄▽ ̄)d…因为 nginx 返回的是没有解释的 index.php 的源码…
这里一定要使用 last 才可以在结束 location / 的 rewrite, 并再次命中 location ~ \.php$ , 将其交给 fastcgi 进行解释.其后返回给浏览器的才是解释过的 html 代码。
关于nginx rewrite的简介到这里就全部讲完了,水平及其有限,请大家指出错漏…
转载自:http://blog.cafeneko.info/2010/10/nginx_rewrite_note/
|
|