WordPress 媒体库上传图片失败:先核 PHP 体积与目录权限,再查安全插件与对象存储

后台媒体库点「添加」或拖一张图进去,进度条走到一半突然停,或直接弹出「HTTP 错误」「超过了最大上传限制」「无法写入上传目录」------列表里没有新附件,前台也就谈不上图。这和今天已经写过的数据库连不上、表单收不到信、HTTPS 重定向环、整站「严重错误」、Woo 结账转圈不是一类故障:站点能打开,卡在 上传链路本身 (PHP 收文件 → 写入 wp-content/uploads → 可选再推对象存储/CDN)。

更稳的顺序是:先看报错文案与 Network 里 async-upload.php / media-new.php 的状态码 → 核 upload_max_filesize、post_max_size、Nginx client_max_body_size(或 Apache LimitRequestBody)→ 再查 uploads 目录属主与权限、open_basedir → 然后排安全/WAF 插件是否拦了上传 POST → 最后才动 OSS/S3 插件与 CDN 回源。别一上来就重装 WordPress 或换主题。

先分清:PHP 体积 vs 目录权限 vs 安全/WAF vs 对象存储

「上传图片失败」常见落在四条线上,别混着改:

  • PHP / Web 服务器体积上限 :单文件超过 upload_max_filesize,或整次 POST 超过 post_max_size / client_max_body_size。表现多为「超过了最大上传限制」、空响应、或 413 Request Entity Too Large。
  • 目录权限 / open_basedir / 磁盘满 :wp-content/uploads(含当年月子目录)不可写,或 open_basedir 把 PHP 关在站点根外。表现多为「无法创建目录」「无法写入文件」,debug.log 里常见 Permission denied。
  • 安全插件 / 主机 WAF / ModSecurity :把 async-upload.php 的 multipart POST 当恶意请求拦掉。表现多为 HTTP 错误、403、或上传请求返回 HTML 挑战页;小图偶发成功、大图/特定扩展名必挂。
  • 对象存储 / CDN 插件:本地其实已写入,但 OSS/S3 同步失败、Bucket 权限、或媒体 URL 被改成外链后回源 404。表现多为后台有附件记录但缩略图裂图,或上传「成功」后前台打不开。

先把媒体库报错原文、浏览器 Network 里那一次上传请求的状态码与响应前几行记下来,再决定是改 php.ini 还是排 WAF,比同时调权限、关插件、换 OSS 更省事。

第一步:看清报错与 Network ------ 别先猜「权限」

用管理员账号打开 媒体 → 添加,F12 → Network,勾选 Preserve log,再上传一张已知大小的测试图(例如 2MB JPEG):

  1. 找到指向 async-upload.php(经典上传)或 admin-ajax.php / REST wp/v2/media(区块编辑器、部分插件)的请求。
  2. 看 Status :
    • 200 且响应是 JSON(含 id、url)→ 服务端接收基本成功,问题多半在后续缩略图生成、对象存储同步或前端显示;
    • 413 → 几乎肯定是 Nginx/Apache/CDN 的请求体上限;
    • 403 / 401 / 406 → 优先怀疑安全插件、WAF、主机防火墙、错误的防盗链;
    • 500 → 翻 wp-content/debug.log 与 PHP-FPM/Nginx error log,常是权限、内存、或某个上传钩子 Fatal;
    • 空 body / 连接重置 → 常见于 post_max_size 过小导致 PHP 直接丢弃 POST,或进程被 OOM 杀掉。
  3. 对照后台文案:
    • 「超过了最大上传限制」→ 先走第二步 PHP/Web 上限;
    • 「无法写入上传目录」→ 第三步权限;
    • 笼统「HTTP 错误」→ 必须靠 Network + 日志,不要只盯媒体库红字。
  4. 顺手在 工具 → 站点健康 → 信息 → 服务器 记下当前 upload_max_filesize、post_max_size、memory_limit、max_execution_time。站点健康里的值与 phpinfo() / 面板不一致时,以 实际处理该请求的 PHP-FPM 池 为准(多版本 PHP 主机很常见)。

生产排查时固定用同一张测试图、同一浏览器无痕会话;每改一处只复测一次上传,并保留改之前的配置截图/备份。扩展名先用 .jpg/.png,排完再测 .webp/.svg(后两者常被安全策略单独限制)。

第二步:对齐 PHP 体积与 Web 服务器请求体上限

目标很简单:浏览器能发出的包,PHP 与前置代理都能完整收进来。最小可核对片段(数值按业务改,示例为 64M):

ini 复制代码
; php.ini 或 .user.ini / 面板「PHP 配置」
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 120
max_input_time = 120

注意:post_max_size 必须 ≥ upload_max_filesize;只改其中一个不够。改完后重启 PHP-FPM(或面板里「重载 PHP」),再回站点健康确认数值已变。

若前面是 Nginx:

nginx 复制代码
# http / server / location 任一合适层级
client_max_body_size 64m;

Apache 则查 LimitRequestBody,以及 mod_security 是否另有请求体限制。Cloudflare / 国内 CDN 也有上传大小档位------边缘直接 413 时,本地 php.ini 怎么改都无效,要在 CDN 控制台或回源规则里放宽,或对 wp-admin 上传路径绕过不合理的边缘限制。

WordPress 侧还会读 wp_max_upload_size()(取 PHP 上限与常量的较小值)。若在 wp-config.php 里自定义过相关过滤,确认没有把上限又压回去。多站点网络管理里另有「最大上传文件大小」选项,子站受网络级限制时,单站 php.ini 再大也传不上去。

改完:清 opcode 缓存(若有)→ 无痕再传同一张图。仍失败且已不是 413/「超过最大上传限制」,带着 Network 证据进权限与安全插件,不要反复只调体积数字。

上传与常见报错的对照(含体积、权限、插件拦截等入口)可放在同一张表里核对,避免只改了一侧:可参考 常见报错速查,把「媒体库 HTTP 错误」和「超过最大上传限制」两条链路一起过一遍。

第三步:uploads 目录权限、属主、open_basedir、磁盘

体积已够但仍写失败时,查文件系统:

  1. 路径 :默认是 wp-content/uploads/YYYY/MM/。确认 wp-content、uploads、当年月目录都存在;若「按月组织」开启但目录创建失败,后台常直接报无法创建目录。
  2. 属主 :PHP-FPM / Apache 运行用户(常见 www-data、nginx、面板虚拟主机用户)必须能在该目录创建文件。属主是 root 而 PHP 是 www 时,表现就是间歇或必然写失败。
  3. 权限 :目录常见 755(或主机要求的 750),文件 644。不要 图省事把 uploads 递归改成 777 长期挂着------排障临时对比可以,确认后应收紧,并记进变更说明。
  4. open_basedir :面板或 php.ini 若把可访问路径收得很死,可能挡在 uploads 或临时目录(upload_tmp_dir / 系统 /tmp)之外。debug.log 里出现 open_basedir restriction 就是这类。
  5. 磁盘与 inode :df -h 与 df -i;磁盘满或 inode 耗尽时,小文件也会「上传失败」,和权限文案容易混淆。
  6. 只读挂载 / 容器卷:容器、部分 PaaS、或误把站点挂成只读时,本地上传必挂;这类环境通常应走对象存储,而不是硬改权限。

用 SSH/面板文件管理建一个测试文件到 uploads 下,能建再删,比只看数字权限更直接。若仅「大图失败、小图成功」且体积已放宽,回头查 max_execution_time、GD/Imagick 生成多尺寸时是否超时或内存不够------那是后处理阶段,不是「没收进 PHP」。

第四步:安全插件、WAF,以及对象存储 / CDN

前三步都干净,Network 仍 403/「HTTP 错误」,或本地文件已在 uploads 但前台裂图,查这一层:

安全 / WAF

  • Wordfence、iThemes、Sucuri、主机 ModSecurity、云 WAF:查封锁日志里是否有 async-upload.php、admin-ajax.php、wp-json/wp/v2/media。
  • 临时对管理员 IP 或上传路径做放行对比(只改一条规则、测完记结果);确认是误杀后再做长期白名单,不要为了传图长期关掉整站防火墙。
  • 禁用「扫描上传文件扩展名过严」类选项时,优先收紧到允许的业务类型(jpg/png/webp/pdf),而不是全开。
  • 登录会话/nonce 过期时,上传也可能变 403;换无痕重新登录再测,排除「权限其实没问题」。

对象存储与 CDN

  • 已装 OSS/S3/COS 插件时:看插件日志是否在「本地保存成功」之后同步失败(密钥、Endpoint、Bucket 地域、ACL)。
  • 「仅本地」开关:排障时可先改为本地存储对比;若本地正常、开 OSS 即裂图,问题在存储插件而非 WP 核心上传。
  • CDN 回源:媒体 URL 被改成 CDN 域名后,回源 Host、鉴权、防盗链配置错误会导致后台能预览、前台 403/404。
  • 迁移过后常见:数据库里附件 URL 仍是旧域名/旧 Bucket;那是 URL 替换问题,不是「这一次上传」失败------别和当前上传故障混治。

每改一类(只动一条 WAF 规则,或只切换 OSS 本地/远程),无痕复测一次上传,并保留规则导出,方便回滚。

验证与回滚

建议验收动作(同一无痕管理员会话):

  1. 上传 1 张小于当前上限的 JPEG → 媒体库出现缩略图,附件详情 URL 可访问。
  2. 再传 1 张接近但不超过上限的大图 → 应成功;再传一张明确超过上限的图 → 应得到清晰的「超过最大上传限制」类提示,而不是含糊 HTTP 错误(说明上限与报错路径一致)。
  3. 区块编辑器与「媒体 → 添加」各测一次,避免只修了经典上传、REST 上传仍被拦。
  4. 若启用了对象存储:确认本地 uploads 与远端对象都有文件,前台 URL 200。
  5. 回滚策略:php.ini / Nginx 体积改动可回退到变更前备份;权限临时 777 必须收紧;WAF 放行规则可单条撤销;OSS 插件先切回本地再查密钥。不要在未备份时批量停用全部安全插件。

排障窗口尽量短;确认无误后关掉临时打开的 WP_DEBUG_LOG 啰嗦级别,恢复生产基线。

验收清单(技术)

  • Network:上传请求(async-upload.php / REST media)状态与响应已核对,能区分 413、403、500、空 body
  • upload_max_filesize ≤ post_max_size;Nginx client_max_body_size(或 Apache 等价)≥ 业务需要;站点健康数值已刷新
  • wp-content/uploads(含年月目录)属主正确、目录可写;open_basedir / 磁盘 / inode 无挡
  • 安全插件与 WAF 日志无误杀上传 POST;必要时仅对管理员或上传路径放行并已复测
  • 对象存储/CDN:本地与远端一致,或已切回本地验证核心上传正常;附件 URL 可访问
  • 小图 + 近上限大图 + 区块编辑器路径均已复测;临时调试项与过宽权限已收紧
相关推荐
海域云SeaArea_1 小时前
Windows Server 2008 R2 从零安装 IIS7.5(二):php.ini 配置
开发语言·windows·php
niucloud-admin2 小时前
NIUCLOUD PHP SAAS上门家政使用文档:服务保障
java·开发语言·php
honsor4 小时前
用 Grafana 做温湿度异常检测:从阈值告警到趋势预测
数据库·mysql·php
开源推荐官4 小时前
多门店 O2O 商城选型复盘:从开发视角对比主流开源系统
java·架构·开源·php
aixingpan16 小时前
aixingpan.cn API开发文档:前端查询夏令时和时区接口指南
前端·php
4SAPI1 天前
多国产模型混合调度与企业级治理生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
大数据·人工智能·架构·php
tommyjiatong1 天前
资安底座连环雷:从 PHP2Shell 到 Node2Shell
node.js·php
青峰讲运维1 天前
OpenStack Heat 编排:用模板一键交付云业务,告别手动点页面
开发语言·php·openstack
协作者1 天前
PHP根本影响不到Domino
php·oidc·domino·notes·inotes·verse