nginx设置pathinfo功能,适配thinkphp5的路由模式。
最近在部署用thinkphp5写的项目到Centos7上,用的是nginx,部署好后发现验证码出不来了,分析一波之后发现nginx无法解析thinkphp的路由规则,所以出现了404错误。网上的教程有些真心不错,有些真的是复制粘贴的。真是走了一大波弯路。于是把自己的经验写一下。
我用的是nginx1.14版本的,这次的部署中,官网的手册真心重要,建议要多多看官网手册。我这次会说明两种形式的写法。
第一种
location / { root /usr/share/nginx/html; index index.php; if (!-e $request_filename) { rewrite ^/meeting/public/(.*)$ /meeting/public/index.php?s=$1 last; } } location ~ \.php { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name; include fastcgi_params; }
这里我们先看看第一个location的rewirite,官网是这样解释的:如果指定的正则表达式与请求URI匹配,则按照替换字符串中的指定更改URI。重写指令按照它们在配置文件中的出现顺序执行。
简单来说就是url根据你的正则表达式进行变换,以上面第一个location块来说,假设url为/meeting/public/aaa/bbb/ccc,根据正则就会替换为 /meeting/public/index.php?s=aaa/bbb/ccc,其中$1就是(.*)$匹配到的,如果你有多个正则,程序将会赋值给$2,$3.........,这样thinkphp就能通过$_GET['s']获取到。然后把请求转给第二个location块,这样就能访问了。
但是这样也是有弊端的,如果我手痒痒,把?s=xxx,写成?g=xxx呢?那当然没什么用了,我记得thinkphp使用的是?s=xxx,或者我直接访问/meeting/public/index.php/aaa/bbb/ccc呢,根据上面的正则表达式,我们就发现也是不行的。因为根本没有访问第一个location,而是直接访问第二个location块,第二个location并没有实现pathinfo的能力。于是访问失败。
这里请注意,如果第二个location是location ~ .php$ {,记得把$去掉。
第二种方法
location / { root /usr/share/nginx/html; index index.php; if (!-e $request_filename) { rewrite ^/meeting/public/(.*)$ /meeting/public/index.php/$1 last; } } location ~ \.php { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; include fastcgi_params; }
第一个location的rewirite表达的正则其实跟前面差不多,假设url为/meeting/public/aaa/bbb/ccc,根据正则就会替换为 /meeting/public/index.php/aaa/bbb/ccc,然后转发给第二个location,第二个location使用了fastcgi_split_path_info。手册对这个的说明是这样的:定义一个正则表达式,该表达式捕获$fastcgi_path_info变量的值。正则表达式应该有两个捕获:第一个捕获成为$fastcgi_script_name变量的值,第二个捕获成为$fastcgi_path_info变量的值。可以看看官网对着两个变量的解释。
这里第二个location通过fastcgi_split_path_info方法,将/meeting/public/index.php/aaa/bbb/ccc根据正则分成两个,一个是/meeting/public/index.php,赋值给$fastcgi_script_name,aaa/bbb/ccc赋值给$fastcgi_path_info。接着将$fastcgi_script_name通过fastcgi_param赋值给SCRIPT_FILENAME,再传递给php服务器。$fastcgi_path_info也是一样。注意,这里的SCRIPT_FILENAME和PATH_INFO是$_SERVER超级全局变量的键名,不能写错。通过这种方法,我们的第二个location块也具备了pathinfo的能力,并且克服了第一种方法的不足之处。