这篇清单要解决的是一件很具体的维护问题:你手头这台 LNMP/LAMP 服务器上,PHP-FPM 的 listen 到底是 unix socket 还是 TCP、绑在回环地址还是全部网卡,以及从外部到底能不能连上 9000。它不只是端口问题——PHP 官方手册明确警告 php-fpm 不应从不可信网络可达,因为能建立 FastCGI 连接的一方可以影响该请求所使用的配置(例如 auto_prepend_file),从而可能执行任意代码,而访问控制主要依靠 listen 与 listen.allowed_clients 来限定,细节可参见 PHP 手册的 FPM 安装说明。动手之前先界定影响范围:站点访问、后台登录、媒体上传、计划任务、插件与主题更新、备份导出这六条路径,改完都要实际走一遍。本清单只处理部署形态带来的暴露面,不涉及 PHP 代码漏洞的利用复现,也不替代补丁管理。

盘点:先看清楚 listen 在听谁
主机侧:三个地方必须对上
第一处是 FPM 自身的池配置。命令在 PHP 服务器上执行,预期每个池输出一行 listen = ...,值可能是 127.0.0.1:9000、/run/php/php8.3-fpm.sock,也可能是不带地址的 9000——最后这种写法等价于监听所有地址。
sudo grep -RInE '^\s*(listen|listen\.allowed_clients|listen\.owner|listen\.group|listen\.mode|user|group)\s*=' \
/etc/php*/fpm/pool.d/ /etc/php-fpm.d/ 2>/dev/null第二处是进程实际监听。配置与现状可能不一致:改了没 reload、跑了多个 pool、被 systemd socket 接管,都会造成差异。
sudo ss -lntp | grep -i php # TCP 监听
sudo ss -lnxp | grep -i php # Unix socket看到 0.0.0.0:9000 或 [::]:9000 就要重点核查;看到 127.0.0.1:9000、[::1]:9000 或 /run/php/phpX.Y-fpm.sock,说明进程侧没有对外。
第三处是 Nginx 的 fastcgi_pass 指向:
sudo nginx -T 2>/dev/null | grep -nE 'fastcgi_pass|listen '预期两边一致:要么都是同一个 socket 路径,要么都是同一个回环地址端口。不一致时站点通常直接 502;如果上游其实指向另一台机器或另一个容器,页面可能"看起来正常",但暴露面反而更大。
容器与云侧:暴露可能不写在 FPM 配置里
PHP-FPM 跑在容器里时,宿主上的 ss 可能什么都看不到,而端口已经发布出去了:
docker port <容器名或ID>
docker inspect -f '{{json .HostConfig.PortBindings}}' <容器名或ID>重点看容器 9000 有没有映射到 0.0.0.0。编排层同理:Docker Compose 的 ports:、Kubernetes 的 NodePort 或 LoadBalancer Service、Ingress 注解,都可能把内部端口带出去;容器之间互相访问应走自定义网络和服务名,不需要发布到宿主。
安全组或主机防火墙是第三层。查入站规则里 9000(或对应端口范围)的来源是否存在 0.0.0.0/0 与 ::/0,IPv6 尤其容易被漏掉,"只改了一半"的暴露大多出在这里。
判断:外部证据和主机证据要同时成立
在授权范围内,从一台外部主机做最朴素的连通性测试:
nc -vz -w 3 <目标IP> 9000
nmap -Pn -p 9000 --script=banner <目标IP> # 可选,banner 未必识别出 FastCGI预期结果是 refused、timed out 或 succeeded 三选一。这里有个容易误判的点:9000 只是 FastCGI 的惯例端口,服务指纹不一定被通用识别脚本认出来,所以判断依据应当是"TCP 能不能建连",而不是"扫描工具有没有认出它是 FastCGI"。
| 外部探测结果 | 主机侧证据 | 结论与动作 |
|---|---|---|
| TCP 可建连 | listen 为 0.0.0.0/::,或端口已发布到宿主 | 确认暴露,按高优先级收敛:先收网络规则,再改监听方式 |
| TCP 可建连 | 只有回环地址或 unix socket | 先别改 FPM。查上游:云负载均衡、NAT/端口转发、反向代理、同网段另一台机器、IPv6 |
| 拒绝或无响应 | listen 为 0.0.0.0/:: | 目前被安全组或防火墙挡着,配置本身仍是敞开的,建议一并收回收敛,避免规则被误改后直接暴露 |
| 拒绝或无响应 | 回环地址或 socket | 现状良好,纳入巡检即可 |
另一个常见误区是"端口号不常见就安全"。暴露面由绑定地址和网络规则共同决定,换个端口并不构成访问控制。
整改:先收网络规则,再改监听方式
顺序上把最浅的动作放前面:先在安全组或防火墙里去掉 9000 的公网入站。这一步不改本机服务,对业务路径通常无感,回滚也只是恢复规则;等外部连通性确认切断后,再动 FPM 配置。改配置前先留备份:
sudo cp -a /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/www.conf.bak.$(date +%F)
sudo cp -a /etc/nginx/sites-enabled/example.conf /etc/nginx/sites-enabled/example.conf.bak.$(date +%F)
把 listen 收敛到 socket 或回环
同机部署的 Web 服务器与 FPM 之间,优先用 unix socket:
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660必须走 TCP 时(容器、跨机部署等场景),绑定回环并限定客户端:
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1listen.allowed_clients 只在 TCP 场景下按来源地址限制客户端,不设置就不会做这层限制。socket 路径里的 PHP 版本号随发行版和 PHP 版本变化,按本机实际存在的文件写,不要照抄示例,两种写法的对照也可参考 DigitalOcean 的 PHP-FPM 与 Nginx 配置教程。socket 是否可用由文件权限决定,不要把 listen.mode 或目录权限放宽到"谁都能连"来绕开连接问题。改完先 sudo systemctl reload php8.3-fpm,reload 对正在处理的请求影响小于 restart。
让 fastcgi_pass 跟上
Nginx 侧要指向同一个目标:
# socket
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
# 回环 TCP
fastcgi_pass 127.0.0.1:9000;改完执行 sudo nginx -t && sudo systemctl reload nginx。如果站点有多个 server 块、多个 PHP 版本,或者用 map、upstream 变量决定 fastcgi_pass,逐一核对,别让某一处继续指向旧的对外地址。
容器与编排的收口
删除宿主的 9000 端口发布,改为容器网络内互通;Kubernetes 里确认没有 9000 的 NodePort、LoadBalancer 或 Ingress 暴露;如果必须由宿主上的 Nginx 访问容器内的 FPM,让容器只监听受限网络并限定来源,不要让宿主 0.0.0.0 直接转发进来。
验证:把六条业务路径真实走一遍
端口收敛不等于站点正常。建议在一段观察窗口内完成下表,窗口至少覆盖一次计划任务周期,尽量选在业务低谷;站点若有支付回调或对外接口,也要算进验证范围。
| 验证项 | 执行位置 | 预期现象 | 异常信号 |
|---|---|---|---|
| 监听形态 | 服务器上 ss -lntp/ss -lnxp | 只剩 socket 或回环地址 | 仍是 0.0.0.0,说明还有池没改或没 reload |
| 外部连通 | 外部主机 nc -vz -w3 <IP> 9000 | 拒绝或超时 | 仍能建连,回查安全组与 IPv6 |
| 首页与后台 | 浏览器 | 首页 200,后台能登录 | 502/504、空白页,查 Nginx error_log 与 FPM 日志 |
| 媒体上传 | 后台上传一张测试图片 | 上传成功、缩略图生成 | 上传失败或缩略图缺失,查写权限与图像扩展 |
| 计划任务 | 需 WP-CLI:wp cron event run --due-now,或等一个周期 | 任务按时触发、无静默堆积 | wp-cron 依赖 HTTP 触发,链路断了往往不报错 |
| 插件与主题更新页 | 后台更新页面 | 页面正常加载 | 加载异常先确认 FPM 连接,不要先怀疑出站限制 |
| 备份导出 | 站点或宿主机脚本 | 文件完整、可解压 | 长耗时请求被截断,查 request_terminate_timeout |
回滚路径要事先写清楚:把备份文件恢复到原路径,依次 reload FPM 与 Nginx,只有在确认是本次变更导致故障时才恢复被收紧的网络规则。观察期内不要删除旧配置副本。
复盘:把三项检查写进日常
这类问题很少因为"配置一次"就永久消失:新装一个面板、发布一个容器镜像、复制一份云模板、给 IPv6 打开一条规则,都可能让 9000 再次可达。建议在巡检清单里固定三条:listen 的当前值、外部 9000 的可达性、fastcgi_pass 与 listen 是否一致。每条变更记录写清时间、改动文件、验证过的业务路径、观察时长和回滚方式。
最后说优先级:当 PHP 核心或常用扩展发布需要紧急处置的安全公告时,互联网暴露的 FastCGI 端口会明显改变处理顺序——先把可达性收掉,再安排版本更新与代码侧修复。
- it2021
- it2021.com
- Kubernetes
- nginx
- nmap
- 访问控制





1F
IPv6 的入站规则确实容易漏查