通常来说, PHP
和 nginx
一块用的时候, PHP
通过 php-fpm
启动处理进程, nginx
通过 fastcgi
协议和 fpm
进行通信.
也就是当客户端请求 .php
的时候, nginx
通过正则匹配文件, 然后设置一些重要的变量, 比如 SCRIPT_FILENAME
这些变量, 然后反向代理给监听的 fpm
处理, 当 fpm
处理完 php
脚本之后将结果返回给 nginx
.
因此对于一般的 php
程序来说, nginx
配置文件只需要简单的加如下内容
location ~* \.php$ {
fastcgi_index index.php;
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
在这里通过 fastcgi_pass
指定 fpm
监听的地址, 而 include fastcgi_params
来包含一个预先定义好的文件, 此文件中通过一系列的 fastcgi_param
指令设置了一大堆 CGI 1.1
协议当中的变量, 这些变量将会在 $_SERVER
魔术变量当中可以获取到.
虽然此方法可以让一般的 php
程序正常运行, 但这会让一些基于 PATH_INFO
来运行的程序产生404, 例如 wordpress 等
要想解决这个问题, 首先得明白什么是 PATH_INFO
, 你去百度搜索一下, 几乎所有人都会跟你说:
PATH_INFO 不是 nginx 的功能, 是 PHP 的功能
对此我只能说, fnndp, PATH_INFO
的确不是 nginx
的功能, 但它也不是 PHP
的功能, 相反, PATH_INFO
是 CGI 1.1
协议当中定义的 Request Meta-Variables
, 具体可以查阅RFC 3875 的 4.1.5
小节.
而一些 PHP
框架大量运用这一特性进行 URL 美化, 例如 /index.php/this/is/path/info
这间接导致了几乎让一大堆人认为 PATH_INFO
就是 PHP
的功能.
知道了什么是 PATH_INFO
之后, 该怎么解决呢? 通常有两种办法.
使用 php 的 cgi.fix_pathinfo 功能 [deprecated]
强烈不推荐此种方式
此方式通过将 php.ini
当中的 cgi.fix_pathinfo
设置为1, 并且在 nginx
中加入一条 fastcgi_param PATH_INFO $fastcgi_script_name;
当开启 fix_pathinfo
之后, php
会根据 CGI 1.1
的规范来分析 SCRIPT_FILENAME
, 从中截取什么是真正的执行脚本, 什么是 PATH_INFO
, 并且修正对应的变量
但这种方法有严重的安全隐患, 会引起脚本注入. 因此强烈不推荐用这种方式, 如果有人教你说用这种方式, 请赶快修正你的配置.
使用 nginx 的 fastcgi_split_path_info 功能
此种方式依赖 nginx
的 fastcgi_split_path_info
指令, 该指令通过正则表达式来对 uri
进行处理, 从中分理出真实脚本文件和 PATH_INFO
, 并根据正则捕获组来设置两个重要变量
1. $fastcgi_script_name
2. $fastcgi_path_info
当使用这种方式的时候, nginx 的配置看起来如下
location ~ ^(.+\.php)(.*)$ {
fastcgi_index index.php;
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
需要注意的是第一行修改了原来的正则, 同时第七行增加了 fastcgi_split_path_info
指令, 而第九行的值改为了正确的 $fastcgi_path_info
.
同时需要注意将 cgi.fix_pathinfo
设置为0.
通过对 nginx
进行调整之后就可以正确的使用 PHP
功能了.
good,很好的介绍了什么是fastcgi_split_path_info