WordPress 上傳圖片出現「HTTP error」:一套真的能找出原因的判斷流程
用一個檢查找出 WordPress 上傳圖片 HTTP error 的真正原因,再對症下藥:防火牆、PHP 記憶體、post_max_size,還是 Imagick。
發布於
「HTTP error」不是原因。它是媒體上傳程式在承認:我把檔案送到伺服器了,但收回來的回應我沒辦法解析成成功 —— 而至少有六種毫無關聯的故障,會產生一模一樣的那幾個字。分不清楚它們,就是為什麼有人花一整個下午在調 upload_max_filesize,而網站真正的問題其實是一條防火牆規則。
最快的出路,是先做一個觀察把六種原因切成兩群,然後每個分支再各查一項。
第一步:讀出真正的回應
打開瀏覽器的開發者工具,切到「網路」分頁,再上傳一次檔案。留意送往 async-upload.php 的那個請求。回來的是什麼,就是你的診斷結果。
| 你看到的內容 | 代表什麼 | 前往 |
|---|---|---|
| 403 或 406,內容是 HTML | 防火牆或 mod_security 拒絕了這個請求 | 分支 A |
| 500、空白回應,或內容被截斷 | PHP 在處理途中崩潰或記憶體用盡 | 分支 B |
| 413 | 請求主體超過了伺服器上限 | 分支 C |
| 200,但內容不是乾淨的 JSON | 有外掛或佈景主題在回應之前先輸出了東西 | 分支 D |
| 請求一直跑不完/逾時 | 執行時間上限,通常發生在大檔案 | 分支 B |
光是看這一眼,就幫你排除掉網路上大半的建議。如果你沒辦法用開發者工具,那就先在 wp-config.php 裡打開記錄:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
重現一次上傳,然後讀 wp-content/debug.log。一行嚴重錯誤就會直接點名是哪個分支。如果那行看起來很難懂,把它貼進錯誤記錄解讀工具 —— 它會把常見的 fatal 特徵對應到實際壞掉的東西。
分支 A —— 防火牆或 mod_security 規則
403 加上一頁 HTML 內容,代表請求根本沒到達 WordPress。擋在前面的某個東西 —— mod_security、Cloudflare、Wordfence、Sucuri,或主機自己的規則集 —— 判定這個 POST 看起來有惡意。
檢查方式:暫時停用所有安全外掛,再上傳一次。如果毫無改變,那條規則就在伺服器層級,你需要請主機商去查失敗上傳當下那個時間點的 mod_security 稽核記錄。他們可以把那條特定的規則 ID 加進白名單。
對這方面你會查到的建議,請誠實一點。那些叫你把這段丟進 .htaccess 的片段:
SecFilterEngine Off
SecFilterScanPOST Off
針對的是 mod_security 1.x,那東西已經接近絕種。在現代伺服器上,這些指令要嘛什麼都不做,要嘛丟出一個 500 讓網站更糟。現代的 mod_security 用的是 SecRuleEngine,而且大多數代管主機本來就禁止以目錄為單位覆寫它。去問主機商,不要亂貼。
分支 B —— PHP 記憶體或執行時間上限
這是最常見的分支,而且線索最明確:它是間歇性的。同一個檔案第三次就傳上去了,或是 2MB 的照片失敗、6MB 的 PDF 卻一路順暢。設定值的硬性上限每次都會用一樣的方式失敗。記憶體不足不會,因為可用的量取決於伺服器同時在忙些什麼。
為什麼特別是圖片:WordPress 不只是把檔案存起來,它還會把圖片解壓成原始像素,以產生每一種中間尺寸 —— medium_large、任何用 add_image_size 註冊的尺寸、如果你有開店還會有 woocommerce_thumbnail,另外圖片若超過 big_image_size_threshold 還會多一份 -scaled 副本。一張 1200 萬像素的 JPEG 在磁碟上只有幾 MB,在記憶體裡卻大約是 48MB,這還沒算縮放用的緩衝區。128MB 的上限很快就沒了。
在 wp-config.php 裡、「stop editing」那行上方把它拉高:
define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );
真正的代價:WP_MEMORY_LIMIT 不可能超過 PHP 本身允許的量。如果你的主機把 memory_limit 鎖在 128M,這個常數就只是裝飾品。要確認真實數值,請看「工具 → 網站健康狀態 → 資訊 → 伺服器」,而不是看你自己寫了什麼。如果天花板是 PHP 自己的上限,那就只有主機商動得了。
分支 C —— 檔案大小與 POST 上限
實際的數字請到「工具 → 網站健康狀態 → 資訊 → 媒體處理」查。有兩個設定值有影響,而大家永遠只改其中一個:
upload_max_filesize—— 單一檔案的上限。post_max_size—— 整個請求主體的上限。
如果整個 POST 超過 post_max_size,PHP 會在 WordPress 執行任何程式碼之前就把請求丟掉。根本沒有東西可以回傳,所以上傳程式收到一個無法解析的回應,然後說「HTTP error」。post_max_size 應該永遠明顯大於 upload_max_filesize。
有一個值得知道的細節:當檔案只超過 upload_max_filesize 時,上傳程式通常會在瀏覽器端就攔下來,並提示檔案超過上傳大小上限 —— 那是比較清楚的訊息。所以一個光禿禿的「HTTP error」,指向 post_max_size 或分支 B 的機率,反而高於大家第一個去調的那個設定。
改在哪裡取決於你的伺服器架構。在 Apache 搭配 mod_php 的環境下,.htaccess 可行:
php_value upload_max_filesize 64M
php_value post_max_size 128M
在 PHP-FPM、LiteSpeed 或 NGINX 上 —— 也就是現在大多數的主機環境 —— 這幾行什麼都不做,還可能丟出 500。請改用主機的 PHP 設定面板,或是 .user.ini 檔案。NGINX 另外還會強制自己的 client_max_body_size,那個只有主機商調得動。
分支 D —— 影像處理函式庫(Imagick 對上 GD)
只要 PHP 擴充功能存在,WordPress 就優先使用 ImageMagick,否則退回 GD。在共享主機上,ImageMagick 經常就是問題所在:它的政策設定檔會限制每次操作的記憶體、磁碟與面積,而當一次縮放踩到其中任何一項上限,程序就會死掉,還不給你有用的訊息。你通常會看到大圖失敗、小圖正常 —— 那看起來很像分支 B,但不是。
強制使用 GD 來驗證這個推論:
add_filter( 'wp_image_editors', function ( $editors ) {
return array( 'WP_Image_Editor_GD' );
} );
把它放進 functions.php 或一個小的 mu-plugin,重試上傳,如果沒有任何改變就把它移除。
老實說說取捨:GD 並不是絕對比較好。它會把完整的未壓縮點陣圖放在 PHP 的記憶體裡,所以面對超大檔案時,它可能撞上 memory_limit,而 ImageMagick —— 有一部分工作是在 PHP 的配額之外進行的 —— 反而會成功。換過去可能只是用一種失敗換另一種失敗。請先把它當成診斷工具,其次才是修復手段。
分支 E —— uploads 目錄權限
在代管主機上很少見,但剛做完搬遷或手動移機之後很常見。如果 wp-content/uploads 對網頁伺服器使用者不可寫入,或是新建立的年/月子目錄繼承到錯誤的擁有者,寫入就會失敗。
ls -ld wp-content/uploads
ls -ld wp-content/uploads/2026/07
目錄通常應該是 755,並且由 PHP 執行時所用的那個使用者擁有。不要因為論壇叫你這樣做就設成 777,那是靠「讓伺服器上每一個帳號都能寫入這個資料夾」來消除症狀,在共享主機上是實實在在的風險曝露。
分支 F —— 檔案名稱
這是清單上最省事的檢查,所以就算它最不可能,也早點做掉。把檔名改成單純的小寫英文字母、數字和連字號 —— 不要有重音符號、單引號、&、# 或非拉丁字元 —— 然後再上傳一次。特殊字元偶爾會觸發某條安全規則,或在編碼不一致的檔案系統上出錯。十秒鐘就能排除。
不要做的事
不要一開始就把所有外掛停用。 那樣干擾很大,而且只測到分支 A 和分支 D。網路請求的回應在五秒內就能告訴你更多。
不要隨便加一行 .htaccess 的 handler 設定。 那些流傳的片段加上 SetHandler 或 AddHandler 指令,都是假設了某一家主機的特定 PHP 設定。在不對的架構上,它們會用一個 500 讓整站掛掉。
不要把「再試一次就成功了」當成已經修好。 時好時壞正是分支 B 的特徵,只要伺服器一忙、或有人傳了更大的檔案,它就會回來。
還是卡住?
如果回應是乾淨的 200 帶著有效的 JSON,上傳卻仍然失敗,那斷點就在檔案落地之後 —— 通常是某個掛在附件處理流程上的外掛丟出嚴重錯誤,或是某個最佳化外掛對外部服務發出自己的請求而逾時。開啟 WP_DEBUG_LOG,重現一次,再把產生的 fatal 丟進錯誤記錄解讀工具,看看真正該負責的是哪個檔案、哪個掛鉤。
FAQ
常見問題
上傳圖片到 WordPress 時出現 HTTP error 是什麼意思?
代表瀏覽器的上傳程式把檔案送到伺服器,但收回來的東西它無法解析成有效的成功回應。這句訊息描述的是症狀,不是原因。PHP 程序崩潰、防火牆攔截、403、以及空白回應,全都會產生一模一樣的那幾個字。
為什麼同一張圖有時候上傳成功、有時候失敗?
時好時壞幾乎都指向記憶體或 CPU 耗盡,而不是設定值的上限。縮放一張大照片需要瞬間吃掉一大塊記憶體,而在共享主機上,可用的量會隨伺服器負載變動。像 post_max_size 這種硬性上限,每一次都會用完全相同的方式失敗。
把 WordPress 從 Imagick 換成 GD 能解決 HTTP error 嗎?
有時候可以,但只對其中一個分支有效。共享主機上的 ImageMagick 常被政策檔或偏低的資源上限限制住,在 GD 能成功的地方它卻會失敗。不過 GD 處理超大檔案時會用掉更多記憶體,所以換過去反而可能讓記憶體造成的失敗更嚴重。
我怎麼知道是不是主機的防火牆擋掉了上傳?
打開瀏覽器的網路開發者工具,上傳檔案,然後看 async-upload.php 回傳的狀態碼。403 或 406 加上一段 HTML 內容,代表是網站應用程式防火牆或 mod_security 規則擋下了這個請求。500 或空白回應則指向 PHP 崩潰。
調高 upload_max_filesize 能解決 HTTP error 嗎?
只有在檔案真的超過上限時才有用,而且通常 post_max_size 更關鍵。如果整個請求主體大於 post_max_size,PHP 會在 WordPress 執行之前就把它丟掉,上傳程式什麼都收不到。只調其中一個,失敗會原封不動留在那裡。
檔案名稱會造成 WordPress HTTP error 嗎?
會,不過這是最少見的分支。重音符號、單引號、&,以及非拉丁文字,都可能觸發某條安全規則,或在檔案系統編碼不一致時讓路徑出錯。改成單純的小寫英文字母、數字和連字號不花任何成本,大約十秒就能排除這個分支。