nginx的rewrite有个很奇特的特性 — rewrite后的url会再次进行rewrite检查,最多重试10次,10次后还没有终止的话就会返回HTTP 500。
用过nginx的朋友都知道location区块,location区块有点像Apache中的RewriteBase,但对于nginx来说location是控制的级别而已,里面的内容不仅仅是rewrite.
这里必须稍微先讲一点location的知识.location是nginx用来处理对同一个server不同的请求地址使用独立的配置的方式。
举例:
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次的上限. 比如这个例子
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标志的解释都是
last – 基本上都用这个Flag
……这他妈坑爹呢!!! 什么叫基本上都用? 什么是不基本的情况? =皿=
有兴趣的可以放狗”基本上都用这个Flag”…
我最终还是在stack overflow找到了答案:
last和break最大的不同在于
– break是终止当前location的rewrite检测,而且不再进行location匹配
– last是终止当前location的rewrite检测,但会继续重试location匹配并处理区块中的rewrite规则
还是这个该死的例子
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就没事了.
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代码.