概览
1、简介
在使用 Nginx 与 PHP 配合作后端服务的过程中,遇到了一个问题,就是配置了 nginx 与 php 后,访问网站地址发现报 404 的错,页面显示 File not found。
从网上查到引起这个问题的原因可能有很多种,本文只介绍我遇到的问题及解决的方法。
2、环境信息
Ubuntu 18.04
Nginx 1.14.0
php 7.2
问题描述
使用 PHP + nginx + MySQL 搭建的博客在配置完成后进行访问时出现报错,页面显示 File not found。如下所示:
Nginx log 日志则是如下错误:
[error] 26579#26579: *393 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: -, server: xxx, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "xxx"
nginx 配置如下
server {
listen 80 default_server;
listen [::]:80;
server_name localhost;
charset utf-8;
...
location / {
root /data/www/blog/website;
index index.php index.html index.htm;
try_files $uri $uri/ /index.php?$args;
}
...
...
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
...
}
原因分析
1、查 Nginx 的问题
当出现这个问题的时候,我第一时间查看了 http status code,发现是 404 状态。一般出现 404 状态都比较好理解了,就是没找到文件,而我又是在访问首页时报的错,所以查找方向就是查看网站根目录有没有 index 文件。
于是我去我设置的网站根目录,即 /data/www/blog/website 目录下查看了,发现有 index.php 文件。再查看文件权限,这个 index.php 只要有读权限就可以了,所以跟权限应该也没问题。
$ ll index.php
-rw-r--r-- 1 root root 405 Sep 20 01:40 index.php
而为了确认这一点,我还将 index.php 改名 index.php.bak,然后在该目录下新建了一个叫 index.html 的文件,文件内容就一个单词 hello。然后发现这个 index.html 能正常访问而且 hello 也正常返回,而且 index.html 与 index.php 的权限都是一模一样的…
正是上面这些操作,让我更加的疑惑了,index 我设置了 index.html index.htm index.php,现在 index.html 能正常访问但 index.php 却不能。所以,我从这里开始认为 nginx 的配置没有问题,问题出在了 php 配置上。于是我把问题成功绕远了,也让我多花了半天时间在 php 的配置上来回纠缠…
2、查 php 的问题
查找 php 的过程又是一翻波折。
(1) 查 php-fpm 的 listen.owner 配置与 Nginx 的 user 配置是否相同,发现是相同的…
(2) 更改 php-fpm 的 listen 配置,我还将 /etc/php/7.2/fpm/pool.d/www.conf 中的 listen 设置在 ip:port 与 unix socket 之间都设置进行测试
unix socket 方式:
listen = /run/php/php7.2-fpm.sock
nginx 配置对应改为
location ~ \.php$ {
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
ipv4:port 方式:
listen = 127.0.0.1:9000
nginx 配置对应改为
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
而这些都对这个问题奏效…
因为我比较相信我的问题是 PHP 相关配置引起的,我还按照网上的解决方法,将 /etc/php/7.2/fpm/pool.d/www.conf 中的 listen.owner 的用户与 nginx /etc/nginx/nginx.conf 的 user 修改为相同进行尝试,仍然不管用…
3、绕了回来
附录中还有一个解决方法,点赞有 40 多个。但我一直没尝试,甚至都没细看,因为这个方法是偏向于 nginx 的配置的问题。而且从大体上看这个解决方案比较麻烦…
可是,我此时是方法都想要尝试一下了,这个方法里也提到了用户与用户组啥的,我直接略过了。而下面有一小段引起我的兴趣,里面写着:
怎么确认你的脚本文件名是否正确呢?
(1) 在 nginx 的 /etc/nginx/nginx.conf 的 http 模块中添加如下代码
log_format scripts '$document_root$fastcgi_script_name > $request';
示例:
http {
...
log_format scripts '$document_root$fastcgi_script_name > $request';
...
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
...
}
(2) 在 nginx 的 conf.d/custom_file.conf 中的 server 添加如下代码
这里不一定都是在 /etc/nginx/conf.d/custom_file.conf 中,custom_file 是一般的配置规范,比如 conf.d/my_blog.conf 用于配置我的博客,根据自己情况更改。
access_log /var/log/nginx/scripts.log scripts;
示例:
$ cat my_website.conf
server {
listen 80 default_server;
listen [::]:80;
server_name localhost;
charset utf-8;
location / {
root /data/www/blog/website;
index index.php index.html index.htm;
try_files $uri $uri/ /index.php?$args;
}
...
access_log /var/log/nginx/my_website_access.log;
error_log /var/log/nginx/my_website_error.log;
access_log /var/log/nginx/scripts.log scripts;
}
然后重载 nginx 后再访问
$ sudo nginx -s reload
查看 /var/log/nginx/scripts.log
/usr/share/nginx/html/index.php > GET / HTTP/1.1
/usr/share/nginx/html/index.php > GET / HTTP/1.1
/usr/share/nginx/html/index.php > GET / HTTP/1.1
/usr/share/nginx/html/index.php > GET / HTTP/1.1
/usr/share/nginx/html/index.php > GET / HTTP/1.1
发现问题了,我在 server 的 loction / {} 中定义了 root 是 /data/www/blog/website,但是日志中看出在请求 / 根路径的时候,nginx 是从 /usr/share/nginx/html 路径在找 index.php 这个文件,而该目录下显然没有 index.php 文件,所以才会报 404 错误(/usr/share/nginx/html 这个目录是 nginx 的默认查找目录)。
这个问题的终极原因终于找到了,是因为我把 /etc/nginx/site-enabled/default 文件删除了,该文件中的 server,导致了整个 Nginx 的配置文件中没有位于 server 模块下的 root 配置,所以导致了这个问题。
注:这个为了查找问题而配置的 log 设置在问题解决后请删除,不然可能会产生日志冗余。
解决方法
将 server 中的子模块 location / {} 中的 root 移到 server 下即可解决这个问题
$ cat my_website.conf
server {
listen 80 default_server;
listen [::]:80;
server_name localhost;
charset utf-8;
root /data/www/blog/website;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
...
access_log /var/log/nginx/my_website_access.log;
error_log /var/log/nginx/my_website_error.log;
}
这个问题也能在 Nginx 官网找到相关的文档,下面我截取了与本问题相关的一小部分,详细的内容可访问附录中的 nginx 官方文档链接查看。
上面提到了,在每一个 location 中配置 root 而不在 server 根节点配置 root 不是一个好的方案,而建议的方案则是下面这种在 server 根节点配置 root,而其他的 location 子节点中可以配置 root 也可以不配置~
匹配优先级是 location 中有 root 配置的,优先匹配 location,如果 location 中都没有匹配到 root,则会匹配 server 根节点下的 root。这样的逻辑不会出现匹配不到 root 的情况。刚好与本问题完全吻合…
附录
1、上文上的问题解决过程基本都参考了下方链接的内容
https://serverfault.com/questions/517190/nginx-1-fastcgi-sent-in-stderr-primary-script-unknown
2、nginx 关于 root 相关的文档
https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/