WordPress 图片上传失败与媒体库整理
WordPress 上传图片失败,报错文案看着五花八门,落到服务器上其实只有四个原因:文件超过了 PHP 允许的大小、上传目录没有写权限、图像处理扩展缺失、处理大图时超时或内存不足。先把报错原文抄下来,对号入座,比在后台反复点「重新上传」有用得多。
先按报错原文分类
WordPress 的媒体上传走 wp-admin/async-upload.php,错误来源分三层:Web 服务器(Nginx/Apache)在请求进入 PHP 之前就拒绝了;PHP 在解析上传时拒绝了;WordPress 拿到文件之后处理失败了。三层给出的文案不一样,处理位置也不同。
| 报错原文 | 出问题的层 | 处理位置 |
|---|---|---|
| 413 Request Entity Too Large | Nginx 请求体限制 | client_max_body_size |
| The uploaded file exceeds the upload_max_filesize directive in php.ini. | PHP 单文件上限 | php.ini 的 upload_max_filesize |
| The uploaded file exceeds the maximum upload size for this site. | WordPress 自身判断 | 多站点网络设置或 upload_size_limit 过滤器 |
| Unable to create directory wp-content/uploads/2026/11. Is its parent directory writable by the server? | 目录权限 | wp-content/uploads 属主与权限 |
| The server cannot process the image. This can happen if the site is busy or cannot allocate enough resources. | 图像处理扩展 / 内存 | GD 或 Imagick、memory_limit |
| Post-processing of the image failed likely because the server is running out of resources. | 图像处理超时 | max_execution_time、memory_limit |
还有一种最麻烦的:页面上什么提示都没有,进度条走完图片就是不出现。这通常是 post_max_size 小于 upload_max_filesize,PHP 把整个 POST 体丢弃了,$_FILES 是空的,WordPress 拿不到错误码,只能静默失败。
四处 PHP 限制要一起改
这四个值互相关联,只改一个经常没效果。经验配比是:post_max_size 至少是 upload_max_filesize 的 1.2 倍,因为一次上传还带着 nonce、action、文章 ID 等字段;memory_limit 至少给到单文件上限的 4 倍,图像解码是内存大户。
upload_max_filesize = 64M
post_max_size = 96M
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
改哪里取决于运行方式:宝塔面板在「软件商店 → PHP → 配置修改」里改,本质是改 /www/server/php/82/etc/php.ini;LNMP 一键包在 /usr/local/php/etc/php.ini;如果是 PHP-FPM 且主机不允许改主配置,可以在网站根目录放一个 .user.ini,写入同样四行,注意它只对 PHP-FPM 生效,且 memory_limit 允许被覆盖。
wp-config.php 里的 WP_MEMORY_LIMIT 只影响 WordPress 进程申请的上限,不能超过 PHP 的 memory_limit。后台「工具 → 站点健康 → 信息 → 服务器」能看到当前生效值,改完先来这里确认,别信面板上显示的数字。
Nginx 的 client_max_body_size
Nginx 默认只允许 1M 的请求体,超过就直接返回 413,请求根本进不到 PHP。这一条在 PHP 配置改好之后仍然会卡住上传,而且后台只会显示一个笼统的上传失败。
server {
client_max_body_size 96m;
client_body_timeout 120s;
# 大文件上传先落临时文件,目录要可写
client_body_temp_path /var/cache/nginx/client_temp;
}
改完执行 nginx -t && nginx -s reload。Apache 对应的是 LimitRequestBody,默认值通常是 0(不限制),所以 Apache 站一般不会卡在这一层。如果站点前面还有 CDN 或 WAF,它们也有自己的请求体上限,这种情况要在 CDN 侧放行,改服务器没用。
目录权限:755 与 644
WordPress 需要写的地方只有 wp-content/uploads 和升级时的临时目录,其余目录不该给写权限。上传失败时报「Is its parent directory writable」,按下面的方式修:
cd /www/wwwroot/example.com
chown -R www:www wp-content
find wp-content -type d -exec chmod 755 {} ;
find wp-content -type f -exec chmod 644 {} ;
chmod -R 755 wp-content/uploads
两个常见误区。一是把整个站点设成 777,上传确实好了,但任何被上传的 PHP 文件都能执行,这是最常见的挂马入口。二是只改 uploads 不改 wp-content,而 WordPress 需要先在 wp-content 下创建 uploads,父目录不可写照样报错。CentOS 上还多一层 SELinux,需要 chcon -R -t httpd_sys_rw_content_t wp-content/uploads,否则权限看着全对,就是写不进去。这条也和 安全加固 直接相关:目录权限过松,是上传型后门能落地执行的前提。
上传成功但前台 404
另一类情况是文件确实写进去了,媒体库能看到,前台 <img> 却 404。先在新标签页直接访问 https://域名/wp-content/uploads/2026/11/photo.jpg:如果这个地址能打开,问题在主题的图片调用;如果也是 404,去看伪静态规则。有人把整站重写规则写成「所有请求都交给 index.php」,连静态资源也一并劫持了,正确写法要先加 location ~* .(jpg|jpeg|png|webp|gif|svg)$ { try_files $uri =404; } 这类放行规则。还有一种可能是 uploads 目录被单独设了 deny all,常见于做过安全加固的站点,规则忘了收窄到 PHP 文件。
WebP 与 GD / Imagick 扩展
症状是图片能上传、媒体库里有条目,但缩略图位置是灰块,点开也显示不出来。这基本可以断定图像处理扩展有问题。先确认装了哪个:
php -m | grep -Ei 'gd|imagick'
php -r "print_r(gd_info());"
gd_info() 的输出里要能看到 WebP Support => 1、JPEG Support => 1、FreeType Support => 1。缺 JPEG 支持时上传 JPG 会失败,缺 WebP 支持时 WordPress 5.8 以后虽然允许上传 WebP,但无法生成其他尺寸。
Imagick 通常比 GD 质量好、内存占用低,处理 4000px 以上的大图更稳。装了 Imagick 之后 WordPress 会优先使用它,可以在「站点健康」里确认。注意 Imagick 有 policy.xml 限制,某些发行版默认禁用了 PDF 和超大尺寸,遇到「Unable to read image」要去看 /etc/ImageMagick-6/policy.xml。内存报错的具体阈值和处理方式,可以参考 内存超限 那一篇。
媒体库按月分目录,别关掉
「设置 → 媒体」里默认勾选了「整理我上传的文件到基于月和年的文件夹」,路径形如 wp-content/uploads/2026/11/photo.jpg。有人为了「路径好看」把它关掉,所有文件平铺在 uploads 下,后果是有代价的。
| 组织方式 | 优点 | 缺点 |
|---|---|---|
| 按年月分目录(默认) | 单目录文件数可控,备份和 rsync 增量快,按时间清理方便 | URL 层级深,迁移时要保持结构 |
| 全部平铺在 uploads | URL 短 | 几万文件后 ls 都卡,FTP 列目录超时 |
| 按分类/项目分目录 | 业务上一目了然 | 需要插件接管,插件停用后路径失效 |
如果确实需要按业务归类(比如产品图、案例图分开),不要改上传路径,而是在媒体库用「分类附件」的方式打标签,路径保持年月结构。改路径的代价会在迁移时体现:数据库里存的 guid 和 _wp_attached_file 都是旧路径,需要整体替换,迁移搬家 时最容易在这里出 404。
批量清理未引用的附件
媒体库越用越乱,通常是因为编辑反复上传同一张图,或者主题演示数据留下几百张没用的图。清理前先做两件事:完整备份数据库和 uploads 目录,以及在测试环境先跑一遍。
先看规模:
SELECT post_mime_type, COUNT(*) AS cnt
FROM wp_posts
WHERE post_type = 'attachment'
GROUP BY post_mime_type
ORDER BY cnt DESC;
再找「没有被用作特色图片」的附件。注意这一步只能排除特色图片,正文里 <img src> 引用的、页面构建器存在 meta 里的、CSS 背景图引用的都查不到,所以下面这条 SQL 的结果只能当候选,不能直接删:
SELECT p.ID, p.post_title, p.guid
FROM wp_posts p
LEFT JOIN wp_postmeta m
ON m.meta_key = '_thumbnail_id' AND m.meta_value = p.ID
WHERE p.post_type = 'attachment'
AND m.meta_id IS NULL
AND p.post_parent = 0
ORDER BY p.ID DESC
LIMIT 200;
更稳的做法是用 WP-CLI 交叉比对正文引用,或者用 Media Cleaner 这类插件先扫描、生成待删清单,人工确认后再执行。删除时建议用 wp post delete <ID> --force,它会一并清掉附件 meta 和生成的各尺寸文件;直接在数据库里 DELETE 只会留下孤儿记录和一堆磁盘垃圾。
上传端能做的预防
- 上传前把长边压到 1600 至 2000px、体积压到 200KB 以内,网页展示用不到 4000px 的原图。图片体积对首屏的影响是直接的,具体数据可以参考 速度优化 里的实测对比。
- 用「设置 → 媒体」里的最大宽高限制,让 WordPress 主动缩放大图,减少派生尺寸的磁盘占用。
- 把 PHP 上传上限设在 32 至 64M 之间,不要为了偶尔传一次视频开到 512M,那等于给攻击者留了更大的上传面。
- 每季度看一次
uploads目录体积,超过 2G 就该做一次清理和归档。 - 把上面这些值和服务器实际生效值记进运维文档,换服务器或重装面板时逐条对一遍,不然同一个问题会再来一次。
媒体库整理本质上是「先备份、再比对、后删除」的活,跳过任何一步都可能删掉正在用的图。如果站点附件已经上万、又没人敢动,可以让信科网络先做一次只读扫描,列出可清理清单再决定,电话 13280692153。