在 Docker 环境里将 Nginx 与 PHP-FPM 拆分为独立容器是最常见的 PHP 部署形态,但许多人在配置后发现 Nginx 返回 502 Bad Gateway,或者浏览器把 index.php 当成文件下载。这类问题通常不是程序代码错误,而是容器间通信与 FastCGI 参数没有对齐所致。下面从网络、配置、路径三个层面说明如何排查与修复。

一、容器网络与 fastcgi_pass 地址
最典型的故障是 Nginx 配置里写了 fastcgi_pass 127.0.0.1:9000;。在容器化环境中,Nginx 容器有自己的回环地址,PHP-FPM 运行在另一个容器,因此 127.0.0.1 指向的是 Nginx 自己,自然连不上 FPM,于是报 502。必须让两个容器处于同一个自定义 bridge 网络,并使用容器名(或服务名)作为主机名访问。
使用 docker-compose 时,声明一个网络并将服务都加入其中。Nginx 的 fastcgi_pass 应写成 php-fpm:9000,这里的 php-fpm 是 compose 服务名,Docker 内置 DNS 会将其解析为对应容器 IP。若手动跑 docker run,则需加 --network mynet 并确保 FPM 容器同名或可通过别名访问。
version: '3'
services:
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./code:/var/www/html
networks:
- appnet
php-fpm:
image: php:fpm-alpine
volumes:
- ./code:/var/www/html
networks:
- appnet
networks:
appnet:
driver: bridge
二、PHP-FPM 监听地址配置
官方 php:fpm 镜像默认监听 9000 端口,且绑定到 0.0.0.0,这适合跨容器访问。但如果你挂载了自定义 www.conf,里面可能出现 listen = /run/php/php-fpm.sock。Unix socket 只能本机进程间通信,跨容器绝对连不上,Nginx 会报连接拒绝。
解决方法是将 FPM 的 listen 改为 0.0.0.0:9000,并确认 listen.allowed_clients 未限制来源(或设为任意)。改完重启 FPM 容器,用 docker exec 进 nginx 容器执行 nc -zv php-fpm 9000 验证端口通断,通了才算网络层就绪。
; 在 php-fpm 的 pool 配置中 [www] listen = 0.0.0.0:9000 listen.allowed_clients = any user = www-data group = www-data
三、Nginx FastCGI 脚本路径映射
即使网络通了,仍可能遇到 "Primary script unknown" 错误。这是因为 Nginx 收到的请求交给 FPM 时,FPM 要在自己容器内的文件系统里找脚本。两个容器挂载了相同的代码目录(如 /var/www/html),但 Nginx 配置里的 root 与 fastcgi_param SCRIPT_FILENAME 若不一致,就会找不到文件。
正确写法是 Nginx 的 root 设为 /var/www/html,并在 location ~ .php$ 块中使用 fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; 或直接用 $document_root$fastcgi_script_name。注意 FPM 容器也必须把代码挂到同样的绝对路径,否则路径对不上。
server {
listen 80;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
四、权限与日志排查
若上述都正确但依旧异常,需看日志。Nginx 侧查 /var/log/nginx/error.log,FPM 侧查 /usr/local/var/log/php-fpm.log 或标准输出。常见还有权限问题:FPM 进程用户(如 www-data)对挂载的代码目录无读权限,导致脚本无法打开。可在宿主机把目录权限设为 755,文件 644,或在 compose 中加 user: "1000:1000" 匹配宿主机用户。
另外,SELinux 开启的系统上,挂载目录可能被阻止访问,可临时设 setenforce 0 验证。确认是 SELinux 原因后,用 chcon 修改上下文而非直接关闭安全模块。经过网络、监听、路径、权限四步核对,Docker 中 Nginx 代理 PHP-FPM 的问题基本都能解决。
# 查看 nginx 容器日志 docker logs nginx_container_name # 进入 fpm 容器看进程与监听 docker exec -it php-fpm_container_name sh netstat -tlnp | grep 9000