【原创】Ubuntu 下Nginx + PHP 出现 File not found 问题

概览

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/

You may also like...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注