WordPress 檔案權限:外掛安裝與 uploads 目錄要分清甚麼

外掛安裝失敗、圖片上傳失敗,或 WordPress 突然要求 FTP/SSH 憑證,畫面上都可能寫着「權限」。但它們不是同一個問題:外掛程式碼寫入 wp-content/plugins,媒體預設寫入 wp-content/uploads,而 wp-config.php 更不應被當成臨時開關。真正要核對的是誰擁有檔案、PHP 用哪個帳戶運行、主機怎樣限制寫入。
不要先改權限數字。先記錄錯誤、精確路徑、owner/group、PHP-FPM 或 suexec 的運行帳戶、mount/ACL/provider rules,再對照主機 setup。需要變更時,只在備份、staging 與精確路徑做最小可回復的改動。不要把 755/644 當所有 hosting 的答案,不要使用 777,也不要對未知目錄做整站 chmod -R。
一、先把三個範圍分開
同一個網站,最少有三個不同性質的檔案範圍:
| 範圍 | 預設位置 | 用途 | 常見寫入者/政策 | 不要混淆 |
|---|---|---|---|---|
| 外掛程式碼 | wp-content/plugins | 安裝、更新、刪除外掛 | 部署帳戶、CI、SFTP 或主機工具;不一定要由 PHP 執行帳戶長期寫入 | 不要為媒體上傳而放寬整個 plugins 目錄 |
| 媒體與附件 | wp-content/uploads | 媒體庫、年份/月份子目錄 | PHP runtime 需要在指定範圍建立及寫入;也可能由物件儲存或 CDN 管理 | 不要為外掛安裝而把 uploads 當成通用寫入區 |
| 連線與 secrets | wp-config.php | 資料庫連線、salts、常數 | 應嚴格限制讀取,一般不應由 web server 寫入 | 不要把它當成排查外掛/上傳時的臨時權限開關 |
實際路徑可能被常數、外掛或主機工具改動;例如 uploads 可以自訂位置,也可能由 CDN/物件儲存接管。排查前先確認「這個網站現在實際用哪個路徑」,不要只憑預設值判斷。
權限範圍圖解 · 先看路徑,再看 runtime
wp-content/plugins
外掛程式碼
- 安裝、更新、刪除外掛
- 可由部署帳戶/CI/SFTP 管理
- 不要把媒體上傳政策搬過來
wp-content/uploads
媒體與附件
- PHP runtime 的有限寫入範圍
- 年份/月份子目錄可按設定建立
- 可寫入不等於可執行 PHP
wp-config.php
連線與 secrets
- 資料庫連線、salts、常數
- 應嚴格限制讀取
- 不是臨時權限開關
圖解替代文字:左欄 plugins 是程式碼部署;中欄 uploads 是 runtime 媒體寫入;右欄 wp-config.php 是 secrets。三欄下方要求先記錄錯誤、路徑、擁有者、runtime、mount/ACL 與主機規則,再在備份、staging 與精確路徑做最小可回復變更。
二、owner 先於 mode:755 不是一個獨立答案
權限數字只描述 owner、group、others 的讀取、寫入、執行組合。同一個 755,放在不同擁有者、不同 PHP 帳戶、不同 mount 之下,效果可以完全不同。WordPress 官方文件也明言:權限會因主機而異,文件只列一般原則,不能覆蓋所有情況 [1]。
官方文件針對幾種 setup 的描述,可以整理成以下對照;這些是情境示例,不是 universal 公式:
| Setup | 官方文件的方向 | 重要 caveat |
|---|---|---|
| 一般標準 setup | 檔案通常由你的 FTP/使用者帳戶擁有;目錄 755、檔案 644 可作對照 | 只適用於該 setup;若 PHP 以不同帳戶運行,數字本身不足 |
| 同一 group 需要寫入 | 文件舉例可用 775/664 | 只在 group 已知、受信任且確實需要時;不是開啟全站寫入的理由 |
| Shared hosting 用 suexec/PHP 以檔案擁有者身分運行 | 目錄 755 或 750;檔案 644 或 640;wp-config.php 440 或 400;不可 777 | 官方把 suexec 視為 shared hosting 的特定情境,不應把數字套到單站或其他 runtime |
| PHP-FPM pool/webserver 與部署帳戶分開;容器或受管平台 | 沒有單一數字可由通用文件推導 | 要記錄實際 runtime user、group、ACL、mount、SELinux/provider policy,再決定寫入方式 |
shared-hosting suexec/owner-runs-PHP
在 suexec 這類 setup,PHP 程序以 PHP 檔案的擁有者身分運行。官方文件的方向是:檔案由實際使用者帳戶擁有,而不是 httpd 程序帳戶;目錄可用 755/750,檔案可用 644/640;wp-config.php 用 440/400;即使是 uploads,也不應給 777。因為 PHP 已是檔案擁有者,它可以按 owner 權限寫入,不需要把目錄開放給所有人 [1]。
這段很重要,但不是叫你把所有主機都當成 suexec。若你的 PHP-FPM pool 使用獨立使用者,或部署人員與 PHP runtime 是兩個帳戶,ownership/group/ACL 的設計就不同;這時直接抄 suexec 的數字,可能仍然不能寫入。
其他 runtime:先問「誰在寫」
以下情況都不應只用一組數字判斷:
- PHP-FPM pool 以
www-data或其他服務帳戶運行,但檔案由 SFTP/部署帳戶擁有。 - 網站跑在容器、唯讀映像、NFS、network mount 或受管平台,檔案系統可能不允許直接寫入。
- 主機使用 ACL、SELinux、AppArmor 或 provider 自訂的目錄政策。
- uploads 已交給物件儲存、CDN 或外部媒體服務,本機目錄不再是唯一寫入點。
在這些情況下,先記錄 id、PHP-FPM pool 的 user/group、精確路徑的 ls -ld、mount 與 ACL,再向主機供應商核對預期設定。沒有這些證據,改 755、775 或 777 都只是猜。
三、外掛安裝:寫入 plugins,不是寫入 uploads
WordPress 官方文件列出三種外掛安裝方式:從外掛目錄自動安裝、在管理員上傳 ZIP,以及用 SFTP 手動把解壓後的外掛資料夾放到 wp-content/plugins [4]。按下 Install Now 時,WordPress 會下載、解包及安裝;這個流程需要對 plugins 目錄有適當的建立/寫入權限。
但「需要寫入」不等於「要讓 web server 永久、廣泛地寫入程式碼目錄」。如果外掛是由部署流程管理,較合理的做法是由 CI/SFTP/主機工具以受控帳戶安裝,而不是把 plugins 變成任何人都可寫。相反,若主機的設計就是由 PHP 帳戶管理外掛,則要按該主機的 ownership 模式處理,而不是從 uploads 的權限推論。
外掛安裝失敗也不一定是 mode 問題。ZIP 大小、PHP upload 上限、磁碟配額、外掛目錄是否為唯讀、容器是否禁止寫入、檔案是否已存在,都可能造成失敗。先看實際錯誤與日誌,再決定是否涉及權限。
FTP/SSH 憑證提示代表甚麼?
WordPress 的 Filesystem API 會在偵測到「寫入檔案的擁有者不匹配」時,改用 FTP 或類似方法;在這種情況下,外掛或主題可能需要向使用者要求 FTP 憑證 [2]。你在管理員看到憑證畫面,代表 WordPress 沒有選擇 direct 寫入,不代表你應該立刻把 FS_METHOD 改成 direct。
憑證提示可能符合主機設計,也可能是 ownership 設定不如預期。正確下一步是核對 PHP runtime、plugins 目錄擁有者與主機建議的方法;不是把密碼貼進文章、設定檔、截圖或工單。若憑證曾經外洩,應依主機流程更換,而不是只改一個常數。
四、uploads:runtime 的有限寫入,不是程式碼執行區
WordPress.org 文件指出,wp-content/uploads 是 WordPress 3.5 起的預設 uploads 路徑;若啟用年份/月份分類,WordPress 會在上傳時建立對應子目錄,前提是父層可寫 [5]。這說明媒體上傳需要的是「在指定範圍內建立及寫入」,不是「整個 wp-content 都開放」。
排查媒體上傳失敗時,至少分開以下幾層:
- 路徑:實際 uploads 路徑是預設值,還是被常數/外掛/主機工具改到別處?
- 父層與目錄:父層是否可寫?年份/月份子目錄是否存在?擁有者是否正確?
- runtime:PHP 以哪個帳戶運行?它是否 owner、同 group,或取得 ACL 權限?
- 資源:磁碟配額、PHP upload 上限、記憶體、圖片處理函式庫是否足夠?
- 外部層:是否使用 CDN、物件儲存、媒體卸載外掛或唯讀容器?錯誤可能不是本機 mode。
另外,「可上傳」不等於「可執行 PHP」。uploads 是可寫入的媒體範圍,不應被當成可執行程式碼的地方;實際的 PHP 執行政策要由伺服器、WAF 或主機規則處理,並向 provider 核對。不要用放寬權限來代替上傳驗證或執行政策。
Hardening 文件亦把檔案權限視為其中一層控制,方向是把可寫範圍鎖到最小,而不是把整個內容目錄開放 [6]。
五、Filesystem API 與 FS_METHOD 的邊界
WordPress 的 Filesystem API 把本機讀寫抽象成 WP_Filesystem,支援不同主機連線方式;官方文件說它能在擁有者不匹配時改用 FTP 或類似方法,並可能要求 credentials [2]。函式參考頁列出優先次序:Direct、SSH2、FTP PHP extension、FTP sockets;有效值是 direct、ssh2、ftpext、ftpsockets [3]。
FS_METHOD 可以覆寫自動選擇,但「可以」不等於「應該」。如果 PHP 帳戶在作業系統層根本沒有寫入權限,強制 direct 不會創造權限;如果主機要求 FTP/SSH,強制 direct 只會令錯誤更難解釋。反過來,若你強制 FTP/SSH,但憑證、路徑、port 或防火牆不正確,也一樣會失敗。
因此,本文不建議直接改 FS_METHOD 作第一步。先讓 WordPress 按主機環境自動選擇,並把實際方法、錯誤與主機建議記錄下來;若 provider 有指定方法,按其文件在 staging 驗證,而不是從搜尋結果抄一個常數。
六、排查順序:先留證據,再改一條
1. 記錄症狀,而不是只記「失敗」
記下時間、操作(安裝哪個外掛/上傳哪個檔案)、完整錯誤文字、發生頁面、WordPress 版本、PHP 版本、主機方案與最近改動。若能看到,亦保存 WordPress debug log 與伺服器錯誤日誌;不要把含帳密或內部路徑的完整日誌公開貼出。
2. 記錄精確路徑與 owner/group
以下只讀檢查可在主機 shell 執行;實際 flag 可能因系統而異:
id
namei -l /actual/path/wp-content/plugins
ls -ld /actual/path/wp-content /actual/path/wp-content/plugins /actual/path/wp-content/uploads
findmnt -T /actual/path/wp-content
getfacl -p /actual/path/wp-content/uploads # 如系統有安裝
這些輸出回答的是:路徑怎樣逐層到達、每一層誰擁有、PHP 以哪個帳戶運行、是否有 mount/ACL 限制。它們不是叫你立即改數字。
3. 對照主機 setup
把以下欄位補齊,再判斷屬哪種情境:
- 檔案與目錄 owner/group;
- PHP SAPI(mod_php、PHP-FPM、CGI/suexec)與實際 runtime user;
- plugins/uploads 是否在不同 mount、不同 ACL 或不同儲存;
- 是否有 SELinux/AppArmor、唯讀層、物件儲存、CDN 或 provider 自訂政策;
- 主機對外掛安裝與媒體上傳的官方建議。
若任何一項不明,先問 provider;不要用「整個 wp-content 可寫」來跳過這個步驟。
4. 在 staging 做最小可回復變更
只有在證據指向某個精確路徑、且主機文件支持時,才在 staging 或備份後變更。一次只改一個範圍(例如只處理 uploads 的父層,或只處理 plugins 的部署帳戶),記錄 before/after 的 owner、group、mode、ACL 與實際測試結果。若沒有明確 rollback 方法,就不要在正式站改。
七、快速決策表
| 症狀 | 先查 | 不要先做 |
|---|---|---|
| 安裝外掛時要求 FTP/SSH | PHP runtime、plugins owner/group、主機建議的 Filesystem 方法 | 直接把 FS_METHOD 改成 direct;把密碼貼進設定或工單 |
| 上傳 ZIP 外掛失敗 | ZIP 大小、PHP upload 上限、磁碟配額、plugins 是否可寫/唯讀 | 把 uploads 或整個 wp-content 改成 777 |
| 媒體上傳失敗 | 實際 uploads 路徑、父層寫入、runtime user、配額、卸載/CDN | 對未知目錄做整站 chmod -R |
| 圖片上傳成功但前台 404 | 檔案是否真的存在、URL/路徑、rewrite、CDN/物件儲存 | 先改權限,未檢查實際檔案與 URL |
| 搬家後出現 0000 或不能寫 | 新主機 owner/group、mount、ACL、provider 政策 | 把舊主機的數字原封不動複製到新主機 |
八、交接時要留下甚麼
把權限問題當成一次可重現的維運事件,而不是一次性的「調好了」。交接紀錄至少包括:實際路徑、owner/group、mode、PHP runtime、Filesystem 方法、錯誤原文、測試動作、before/after、改動人、時間、備份位置、rollback 方法與 provider 工單編號。若涉及 staging,可配合上線前 staging 檢查;若近期做過更新,先看更新後安全訊號,不要把更新、權限與快取混成一個問題。需要演練回復時,另看備份還原演練。
要整理外掛安裝與 uploads 的權限邊界?
若你需要按實際主機 runtime、owner/group、mount/ACL 與 provider 規則整理排查紀錄,可聯絡 Managed-WP.™ PRO(WordPress 專業代管服務)。
常見問題
755/644 是否適用於所有 WordPress 主機?
不是。官方文件明言權限因主機而異,755/644 只是一般標準 setup 的對照;suexec、PHP-FPM 獨立帳戶、容器、ACL、NFS 或受管平台都可能不同。先確認 owner 與 runtime,再談數字。
外掛安裝要求 FTP/SSH,是否應把 FS_METHOD 改成 direct?
不建議把改常數當第一步。憑證提示代表 WordPress 沒有選擇 direct 寫入,原因可能是 ownership、runtime 或主機設計;先查實際帳戶與 provider 建議。強制 direct 不會創造作業系統權限。
uploads 目錄可以設 777 嗎?
不建議。777 會把寫入/執行權限開放給過多對象;官方 suexec 段落亦明確說 uploads 不應給 777。應按 runtime user、owner/group、ACL 與 provider 規則,給予指定範圍所需的最小寫入。
外掛安裝與媒體上傳可以用同一套權限嗎?
不應假設可以。plugins 是程式碼安裝路徑,uploads 是 runtime 媒體寫入路徑;兩者可能有不同擁有者、不同部署流程與不同主機政策。分開記錄,才不會為了其中一個而放寬另一個。
可以用 chmod -R 一次改好整個 wp-content 嗎?
不建議。遞迴改動會影響主題、外掛、快取、上傳與其他未知檔案,亦難以確認哪個改動真正有效或如何 rollback。應在備份與 staging 下,只針對已確認的精確路徑作最小變更。
FTP/SSH 密碼可以寫在 wp-config.php 或文章嗎?
不應把密碼寫入文章、截圖、工單或可提交的設定片段。使用主機提供的安全憑證流程或 secret 管理方式;若曾外洩,依 provider 流程更換。排查時只需記錄使用了哪種方法,不需要把憑證內容複製到交付物。
資料來源
- Changing File Permissions
- Filesystem
- get_filesystem_method()
- Manage Plugins
- Settings Media screen
- Hardening WordPress
編輯說明:本文由 Managed-WP.™ PRO 編輯團隊整理官方文件。權限數字與 setup 例子只作對照,不是所有主機的通用建議;本文不指示使用 777、整站遞迴改權限或直接改 FS_METHOD,也不承諾任何主機、外掛、CDN、SLA 或效能結果。實際 owner、runtime、mount、ACL 與 provider policy 應以你的主機環境為準。