为出海而探索,为选择多想一步。CROSSBORDER GEEK / K8N.COM
K8N跨境极客CrossBorder Geek

教程

PHP 生产环境安全加固指南:php.ini 与 PHP-FPM 配置基线

先说思路

PHP 生产加固:代码纸与检查清单示意 AI 操作示意图·以官网实际界面为准

前阵子 php2shell 那两个高危 RCE 闹得紧,很多人升级完版本就以为万事大吉。其实升级只是把已知漏洞堵上,生产环境的安全基线是另一回事——开发时图方便开的那些东西,到了线上全是攻击面。这篇就是我自己每次新部署 PHP 站点时会过一遍的加固清单,按 php.ini、PHP-FPM、OPcache 三块来,命令都能直接抄。

1. 开发配置 ≠ 生产配置

最常见的翻车点:直接把开发机的 php.ini 复制到生产,或者干脆没动过默认配置。开发环境开着 display_errors、暴露版本号,是为了调试方便;生产环境这些会把报错堆栈、路径、数据库信息直接吐给访问者。

先定位你的配置文件:

php --ini          # 看 Loaded Configuration File 指向哪
php -i | grep disable_functions   # 看当前禁用了哪些函数

2. php.ini 关键项

下面这几项是我每次必改的,含义和建议值如下(默认值来自官方手册,以官网当前页面为准):

配置项 作用 生产建议
expose_php 在 HTTP 响应头 X-Powered-By 里暴露 PHP 版本 Off,别让扫描器一眼知道你版本
display_errors 把错误直接输出到页面 Off,错误只进日志
log_errors / error_log 错误写日志文件 On,并指定可写日志路径
disable_functions 禁用危险的内部函数 见下方说明
open_basedir 限制 PHP 只能访问指定目录树 设为站点根目录 + 临时目录
upload_max_filesize 单文件上传上限(默认 2M) 按业务定,且必须小于 post_max_size

关于 disable_functions,官方手册明确写了它只能禁用内部函数、必须写在 php.ini 里,而且可以被绕过,不能当成共享主机的唯一安全措施。常见的一组是:

expose_php = Off
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
open_basedir = /var/www/example.com/:/tmp/
upload_max_filesize = 8M
post_max_size = 12M

注意 open_basedir 官方提示它只是"额外的安全网,并不全面",而且开启后会禁用 realpath 缓存带来一点性能损耗——这是用性能换隔离,值不值自己权衡。

3. PHP-FPM 池配置

PHP-FPM 池与权限盾牌示意 AI 操作示意图·以官网实际界面为准

FPM 的风险点在于:一个暴露在公网的 FastCGI 端点等于允许任意代码执行(官方手册原话)。所以池配置的核心是"别让别人能连进来"。

[example.com]
user = www-data
group = www-data
listen = /run/php/example.sock     # 优先 unix socket,别用 127.0.0.1:9000 裸奔
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20

几个要点:

  • listen 用 unix socket,而不是 TCP 端口。官方建议 web 服务器和 PHP 在同一台机器时优先 socket,权限靠 listen.owner/group/mode 控制;实在要用 TCP,必须配 listen.allowed_clients 限制来源 IP,否则默认接受任意地址连接。
  • 运行用户用低权限账号,站点目录权限对应收,别让 PHP 进程以 root 跑。
  • pm.max_children 别拍脑袋。它是同时服务的最大子进程数,按内存估:单进程内存 × max_children 不能超过机器可用内存。官方给的是 mandatory 项,pm 有 static/dynamic/ondemand 三种,小站点用 ondemand 更省内存。
每个站点都要单独建 FPM 池吗?

不一定。官方手册提醒"池不是安全机制"——所有池共用同一个 OPcache 实例,池之间并不完全隔离。多站点共享一台机器时,分池主要是为了按站点设不同用户和 open_basedir,而不是指望它做强隔离。真要强隔离,得上容器或独立账号体系。

4. OPcache 生产建议

OPcache 默认 opcache.enable = 1,共享内存默认 128MB。生产环境建议保持开启,它是免费的性能提升:

opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.revalidate_freq = 60

注意官方手册里那个坑:interned_strings_buffer 的内存是从 opcache.memory_consumption 里扣的,设大了留给字节码缓存的空间就小了。改完配置要重启 FPM 才生效。

数据中心运维人员巡检实拍

5. 一份可直接抄的检查清单

上线前对着过一遍:

  • [ ] php --ini 确认改的是生产真正加载的那个文件
  • [ ] expose_php=Off、display_errors=Off、错误进日志
  • [ ] disable_functions 已禁用高危执行类函数
  • [ ] open_basedir 限定到站点目录
  • [ ] FPM listen 用 unix socket 或配了 listen.allowed_clients
  • [ ] FPM 运行用户非 root,目录权限对应收紧
  • [ ] pm.max_children 按内存算过,不是默认值瞎留
  • [ ] OPcache 开启,改完重启 FPM 验证

官方资料

资料核对日期:2026-10-25

最近一周热门文章

按近 7 天访问热度排序,热度积累前优先展示新文章。

当前栏目最新文章

教程 · 继续阅读同栏目的新内容

一起交流

0 条留言

还没有留言,来分享你的想法吧。

LEAVE A NOTE

你的经验,也很重要。

无需注册或登录。请勿公开邮箱密码、证件、银行卡号或其他敏感信息。

更多阅读
K8N · CrossBorder Geek

给出海玩家、独立开发者、跨境博主与外贸人的选型笔记。

关于这个博客 ↗