Nginx 把 PHP 文件当下载而不是执行,怎么办?
Nginx 下载 .php 而非执行的根因与修复:缺少 PHP-FPM 的 fastcgi 配置块;完整 location ~ \.php$ 示例、socket 路径确认、安全提醒(源码泄露风险)与 FAQ。
根因:Nginx 本身不会执行 PHP——它需要把 .php 请求转发给 PHP-FPM(FastCGI)。你的配置里缺少这个转发块,于是 PHP 文件被当普通静态文件直接返回,浏览器就弹下载了。 这不仅是功能故障,还是源码泄露安全事故,应立即修复。
修复:补上 PHP-FPM 转发配置
在 server 块中加入:
location ~ \.php$ {
include fastcgi_params; # 或 fastcgi.conf
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; # 与 PHP-FPM 实际 socket 一致
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
}
同时在 server 块里确保 index 包含 PHP:
index index.php index.html;
改完 nginx -t && systemctl reload nginx。
逐项确认清单
-
PHP-FPM 装了且在跑:
sudo apt install php-fpm # Ubuntu/Debian systemctl status php8.2-fpm # 确认 active -
socket 路径对得上:
grep '^listen' /etc/php/*/fpm/pool.d/www.conf的输出必须和fastcgi_pass写的一致。PHP 升级后路径里的版本号常变(php7.4-fpm.sock→php8.2-fpm.sock),是最高频翻车点。 -
location 顺序没被抢:确认没有更优先的正则 location 把
.php请求截走。
为什么这是安全事故
当 Nginx 把 PHP 当文件下载时,用户拿到的是源代码——数据库密码、API 密钥、内部逻辑全部裸奔。发现此问题的第一反应应该是:立即修复 + 轮换该代码库里的所有凭据,而不只是"让它能跑"。
顺手加固:只放行入口文件
更严的写法,只允许执行明确的入口文件,杜绝上传目录里的 PHP 被执行:
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# 上传目录禁执行
location ~* ^/uploads/.*\.php$ { deny all; }
观测云对照
这类故障在日志里的特征很明显:.php 的 GET 请求返回 200 且 Content-Type: application/octet-stream、响应体异常大。把 Nginx 访问日志接入观测云,按 URI 后缀 × 状态码 × 响应类型聚合——PHP 被当文件返回的瞬间就能从数据里看出来;对这类异常模式配告警,配置回滚或发布事故引入的"源码裸奔"分钟级止损。
常见问题(FAQ)
Q:加了配置变成 502 Bad Gateway?
A:功能修好了,轮到连接问题——socket 路径不对或权限不足(www-data 读不到 fpm.sock)。按"Nginx 连接 PHP-FPM socket 报 Permission denied"那篇的清单查。
Q:用了 Apache 也需要这个吗?
A:不用。Apache 的 mod_php 直接内嵌执行,只有 Nginx(以及 Caddy 等)需要显式接 PHP-FPM。
Q:能用 TCP 端口代替 socket 吗?
A:可以:PHP-FPM 配 listen = 127.0.0.1:9000,Nginx 写 fastcgi_pass 127.0.0.1:9000;。容器化部署里 PHP-FPM 在另一个容器时只能走 TCP。