现在越来越多的server使用nginx做前端,在使用php的项目中越来越多的使用单一入口文件,而且很多时候希望隐藏这个入口文件,生成一个漂亮而简洁的url。以前在apache下是使用一个独立的rewrite模块,或者使用.htaccess文件实现重定向,nginx中需要小小的配置一下。
比如对于一个请求: http://localhost/index.php?r=c/a&p=v 这样,我们希望请求到 http://localhost/c/a?p=v。
比较老的做法是配置一个rewrite,以前的项目一般都是这么做的:
location / { |
但是这样做有可能会导致循环重定向而返回500错误页面。并且有一个问题,就是静态文件也会被转发到index.php。
静态文件转发到php-fpm不仅导致做了无用的工作,而且对于允许用户上传图片/文件的应用可能导致安全问题。这是非常不推荐的用法。
为了解决上面的问题,我试过一个稍微麻烦但是能正常工作的方案:
if ($request_uri !~ "/static(.*)" ) { |
这里假设静态文件都存放在/static目录下。这样可以让静态资源文件不被转发。但是这个方案啊,总觉得不那么优雅。
今天在解决一个问题的时候正好又查了一下,发现现在更多的使用的try_files配置来做重写。try_files的语法如下:
try_files file ... uri |
其作用域是server 和location,并且不能放在if条件里面。最常用的用法是:
try_files $uri $uri/ index.php |
下面简单解释一下,try_files顾名思义就是尝试读取文件,正是对于请求的脚本不存在的情况,给nginx一个尝试读取脚本的策略。第一个是$uri
就是读取uri指定的文件,如果不存在就把请求的看作目录,查找目录下有没有默认index文件(一般配置为index.html, index.htm, index.php);如果有则读取这个文件。对于try_files的最后一个参数,会作一个 内部重定向/fallback,这个内部重定向可以看作一个内部子请求,会重新被nginx配置match一遍。注意,只有最后一个参数会发起子请求
在我们的配置里面最后一个参数是index.php这样,会发起一轮新的match会被nginx配置里面的 location ~ .*\.(php|php5).*$ {}
catch然后进行转发到php-fpm解析。
当请求是静态文件时,因为能直接match的$uri,所以直接就返回静态文件的内容了,对于页面的请求就会转发到index.php.
这样就解决了index转发和静态文件不转发的问题,键值优雅得多了。但是有一个问题,那就是参数。
对一般的web框架来说,其请求的url一都是这样样式的:
http://host.com/index.php?r=controller/action¶m1=value1&... |
需要注意的是,nginx在匹配try_files的最后一项时不会自动把args(也就是querystring)转发出去,需要手动加上,所以对于上面的需求,可以使用下面的配置:
try_files $uri $uri/ /index.php?r=$uri&$args |
这样,对于http://host.com/user/login?username=wuxu&passwd=123
,$uri匹配到user/login
,$args匹配到username=wuxu&password=123
,经过try_files配置之后就是http://host.com/index.php?r=user/login&username=wuxu&password=123
了。(当然这里只是用login做示例,实际中的login可不能用GET传递参数哦)。
对于我们自己实现的框架可能有不同的路由方法,相应的修改try_files的策略就可以了。
当然try_files的最后一个参数还可以更复杂,具体可以看nginx的参考文档。
完。
参考: