Wordpress

.htaccess 設定有哪些重點?轉址、封鎖 AI 爬蟲與快取效能整理

想像一張貼在大門口的規則表,寫著誰能進哪一扇門、哪些訪客要被擋在外面、東西要用多快的速度送到客人手上。這張表在每一個 WordPress 網站上都真實存在,只是沒有幾個人真的打開它看過,它叫 .htaccess,就躺在網站根目錄,跟 index.php 放在同一層。

多數人唯一接觸到它的時刻,是固定網址突然壞掉、文章頁全部跳出 404 的那一刻。這時候 WordPress 早就自己在 .htaccess 裡寫好四行規則,負責讓固定網址正常運作,多數站主一輩子也不會多看一眼。可是這個檔案能做的事遠不只這一件,轉址舊網址、把 http 與不帶 www 的版本統一成單一網址、鎖住後台登入來源、擋掉這兩年才冒出來的 AI 訓練爬蟲,甚至決定圖片能不能被別人的網站直接盜連,.htaccess 設定其實是從安全到效能都能碰到的一整組開關,只是散落在不同教學裡,很少有人一次看完。

只是這張表改錯一行,網站可能直接白屏,所以在動手加任何規則之前,得先搞懂這個檔案本身在做什麼、以及它為什麼長這樣。

.htaccess 設定從網址轉址、安全防護到快取效能都能碰到,是網站根目錄裡影響全站的一份規則表
.htaccess 設定分成三大類:網址與轉址、安全防護、效能加速,同一份檔案就能一次管到。

.htaccess 是什麼?WordPress 寫入的四行預設規則

.htaccess 這個檔名本身就有點特別,它沒有副檔名,只有一個看起來像副檔名的字尾,這也是很多人用 FTP 軟體登入時翻遍整個目錄都找不到它的原因,大部分 FTP 客戶端預設會隱藏這種以句點開頭的隱藏檔案,得先在設定裡打開「顯示隱藏檔案」才看得到。找到之後,它會躺在網站根目錄,跟 wp-config.phpindex.php 同一層,不會出現在子目錄裡。

打開一個剛安裝好、固定網址已經設定過的 WordPress 網站的 .htaccess,會看到類似下面這四行,夾在 # BEGIN WordPress# END WordPress 兩行標記之間:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPressCode language: plaintext (plaintext)

這幾行規則做的事只有一件,只要瀏覽器要求的網址在伺服器上找不到對應的實體檔案或目錄,就把整個請求轉去給 index.php 處理,讓 WordPress 自己判斷這個網址該顯示哪篇文章、哪個分類頁。這也是固定網址能寫成類似「年份加月份加文章標題」這種漂亮格式,卻不需要真的在硬碟上建一堆對應資料夾的原因。RewriteRule ^index\.php$ - [L] 這一行是後來才加進去的,早期版本的 WordPress 沒有它,作用是讓對 index.php 本身的請求直接放行、不再往下比對其他規則,避免跟某些外掛自己加的 rewrite 規則互相衝突。

如果網站不是裝在網域根目錄,而是裝在子目錄底下,RewriteBase 與最後一條 RewriteRule 都要跟著改成子目錄路徑:

RewriteBase /子目錄/
...
RewriteRule . /子目錄/index.php [L]Code language: plaintext (plaintext)

接下來每一節要加的自訂規則,都要寫在 # BEGIN WordPress# END WordPress 這段標記之外,原因很直接,WordPress 每次儲存固定網址設定,就會重新產生標記之間的內容,寫在裡面的自訂規則會被整段覆蓋掉、憑空消失。多數人習慣把自訂規則加在標記區塊的上方或下方,兩種位置都可以。

動手改之前先備份,改完後要確認網站沒有跳出錯誤

.htaccess 之前,先把目前的版本下載一份存到本機,這是整篇文章裡最重要的一個步驟,卻也最常被跳過。如果打開檔案卻發現裡面什麼都沒有,代表 WordPress 還沒寫入預設規則,這不影響備份這件事本身,之後只要走一次固定網址設定頁的儲存動作,那四行規則就會自動補上。備份好之後,順手確認一下檔案權限,一般建議設在 644,也就是擁有者可讀可寫、其他人只能讀取,權限開太寬鬆等於多留一個被竄改的入口。

真正動手加規則時,一次只加一段、存檔之後立刻重新整理網站首頁確認一次,不要把好幾段規則一次貼上去再測試。原因很簡單,.htaccess 只要有一行語法寫錯,輕則那一段規則不生效,重則整個網站直接跳出 500 Internal Server Error 或一片空白,這時候如果同時貼了五、六段規則,很難第一時間判斷是哪一段出的問題,逐段測試才抓得出來是哪一行寫壞了。

一旦看到 500 錯誤或白畫面,第一個動作是把備份檔案傳回去覆蓋,或者更直接一點,先把 .htaccess 整個刪掉,網站不會因此完全打不開,WordPress 會在下次有人存取固定網址設定頁面時,自動重建最基本的預設區塊,先讓網站恢復運作,再回頭慢慢排查是哪一段自訂規則出的問題。等改完確認網站救回來之後,除了看首頁正不正常,也要點幾篇文章頁、分類頁,以及後台登入頁,確認固定網址、轉址、後台存取這幾件事都還正常運作,避免自己不小心把自己鎖在後台外面。

301 轉址換掉網址,排名照樣保得住

網址改掉是每個經營一段時間的網站遲早會遇到的事,文章 slug 調整過、舊分類頁整個下架、甚至整個網域換掉,都需要把舊網址的訪客與搜尋引擎導去新網址。這時候轉址用的狀態碼很關鍵,301 代表永久轉址,搜尋引擎收到之後,會把舊網址原本累積的排名權重轉移到新網址上;302 代表暫時轉址,搜尋引擎會把它當成一個過渡性的變動,不會轉移權重,舊網址的排名累積很可能就這樣白白流失。換網址時該用哪一種,答案很明確,永遠是 301

.htaccess 裡處理轉址主要靠兩個指令,Redirectmod_alias 模組提供,語法簡單,適合處理單一頁面對單一頁面的轉址;RewriteRulemod_rewrite 模組提供,能處理正則表達式與萬用字元,適合處理一整批網址的轉址。兩者可以放在同一個檔案裡,但執行順序不太一樣,Redirect 這類簡單轉址規則通常會先被處理過。

第一種,單頁轉址只要一行 Redirect 301

最常見的情境是單一頁面的網址改了,可能是文章 slug 調整過,也可能是某個頁面搬到新路徑,這種情況不需要動用正則表達式,一行 Redirect 301 就能解決:

Redirect 301 /舊頁面路徑 https://你的網域/新頁面路徑Code language: plaintext (plaintext)

這一行的意思是,只要有人存取「舊頁面路徑」這個網址,不管是搜尋引擎的爬蟲還是真人訪客,都會被直接導向後面指定的新網址,而且是用 301 告訴搜尋引擎這是永久性的搬遷。這類規則寫在 # END WordPress 之後最不容易跟固定網址規則的比對順序衝突,之後每加一個新的單頁轉址,就在這行下面再補一行即可。

第二種,目錄搬遷用萬用字元批次轉址

如果搬動的不是一個頁面,而是整個資料夾,例如一批舊分類頁、或者一整個舊產品目錄整批換了新路徑,逐一寫 Redirect 太費工也不切實際,這時候該用 RewriteRule 搭配萬用字元,一次處理整個目錄底下的所有子路徑:

RewriteRule ^old-folder/(.*)$ /new-folder/$1 [R=301,L]Code language: plaintext (plaintext)

這一行規則會把 old-folder 底下任何子路徑,原封不動導到 new-folder 底下對應的位置。前段小括號 (.*) 抓到的內容會存進 $1,接到新目錄路徑後面,等於把整個資料夾的結構原樣搬過去,不需要為底下每一個子頁面各寫一行規則。

第三種,換網域時把整站導到新網址

換網域名稱是規模最大的一種轉址,這條規則會影響全站每一個路徑,寫法反而是三種裡最簡單的一行:

Redirect 301 / https://新網域.com/Code language: plaintext (plaintext)

這一行放在檔案最上方或 # END WordPress 之後都可以,但要確保它排在其他會攔截同樣路徑的規則之前執行,否則可能永遠輪不到它。這條規則務必只在真的要永久搬遷網域時才貼上去,開發測試階段千萬別加,會把整個還沒上線的網站整批導走。換完網域上線後,還要記得同步更新 WordPress 後台網站位址的設定,沒有同步更新的話,這條轉址規則會跟後台設定互相衝突,形成訪客怎麼點都回到原地的轉址迴圈。

HTTP、www 與非 www 收斂成單一網址

同一個網站如果 http://https://、帶 www.、不帶 www. 這幾種寫法全部都能打開,看起來像是多一份彈性,實際上是把同一份內容拆成好幾個不同的網址。這對搜尋引擎來說等於內容重複,連結權重也會被這幾個版本分散掉;對讀者來說,分享出去的連結每次都不太一樣,也不夠專業。正確的做法是選定一個最終版本,把其他所有變體全部收斂到那一個網址。

要注意的是,很多教學會分兩段轉址,先轉 https 再轉 www,結果雖然一樣,但讀者的瀏覽器要多繞一次才會到達最終網址,多一次來回就多一點延遲。比較好的做法是把兩個判斷條件合併進同一組規則,用 [OR] 把「不是 HTTPS」跟「主機名稱不是 www 開頭」放在一起判斷,只要中一個條件,就一次性導到最終版本:

RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.你的網域.com/$1 [L,R=301]Code language: plaintext (plaintext)

上面這段以「帶 www」當最終版本示範,如果想選「不帶 www」當最終版本,把第二條 RewriteCond 改成 RewriteCond %{HTTP_HOST} ^www\. [NC]RewriteRule 的目標網址也要拿掉 www.。至於要選帶 www 還是不帶,兩種都可以,沒有絕對的對錯,選定一種之後全站統一即可。這條規則建議寫在 # BEGIN WordPress 之前,確保網址在 WordPress 自己的 rewrite 規則開始比對之前,就已經先被正規化成同一個版本。

Apache 存取控制語法已經改用 Require

限制誰能存取、保護敏感檔案,這兩件事都建立在同一組語法基礎上,先把這組語法搞懂,後面的規則才看得懂。網路上流傳已久的教學,很多還在用 OrderAllowDeny 這組指令控制存取權限,但 Apache 官方文件已經明確把這組由 mod_access_compat 模組提供的指令列為棄用狀態,未來版本會直接拿掉,也建議避開還在教這幾個指令的舊教學,改用新的 Require 語法。

新舊語法的對應關係不複雜,Allow from all 對應 Require all grantedDeny from all 對應 Require all denied。原本用 IP 做限制的寫法也是同一套邏輯,只是換了指令:

用途舊語法新語法
全部允許Allow from allRequire all granted
全部拒絕Deny from allRequire all denied
限制特定網段Order deny,allowDeny from allAllow from 192.168.1.0/24Require all deniedRequire ip 192.168.1.0/24

Require ip 後面能接的格式也很彈性,完整 IP(Require ip 10.2.3.4)、部分 IP(Require ip 172.20,代表比對整個 172.20.0.0/16 網段)、CIDR 或 netmask(Require ip 192.168.1.0/24Require ip 192.168.1.0/255.255.255.0)都支援,連 IPv6(Require ip 2001:db8::a00:20ff:fea7:ccea)也可以直接寫。如果情境是反過來,要「除了某個 IP 之外全部允許」,得用 Require not ip 搭配 <RequireAll> 把條件包起來,因為單獨一條 not 條件本身不構成允許或拒絕的判斷,一定要跟至少一條能給出明確允許或拒絕結果的條件放在同一個區塊裡:

<RequireAll>
Require all granted
Require not ip 10.252.46.165
</RequireAll>Code language: plaintext (plaintext)

封鎖惡意存取與新一代 AI 爬蟲的規則

後台登入頁、不正常的請求方式,還有這兩年才真正變成課題的新對象、也就是 AI 爬蟲,都屬於「誰不該進來」該處理的範圍。GPTBot、ClaudeBot 這類爬蟲會為了訓練模型或即時回答問題,大量抓取網站上的內容,站主得自己決定要放行還是封鎖,這已經不是單純的資安問題,也牽動網站的內容會不會被 AI 工具引用。

守規矩的 AI 爬蟲會遵守網站根目錄的 robots.txt,但 robots.txt 說到底只是一份君子協定,沒有任何強制力,真的想擋,得在伺服器層級直接用 .htaccess 判斷 User-Agent 字串來擋。

限制 wp-login.php 的來源 IP

只讓自己與其他管理員的固定 IP 能連到登入頁面,其他來源一律拒絕存取,是擋暴力破解密碼最直接的一步,用前一節的新語法寫法如下:

<Files wp-login.php>
Require ip 你的IP位址 你的第二個IP位址
</Files>Code language: plaintext (plaintext)

多組 IP 用空格分隔並列在同一行即可。這裡有個提醒,如果用的是家用網路常見的動態 IP,也就是浮動制,IP 位址換了會連自己都被擋在登入頁外面,設定前最好先確認自己的 IP 是不是固定的,或者留一個備援存取方式,例如透過主機商後台,或者額外登記第二組固定 IP。

擋掉不必要的 HTTP 請求方法

一般讀者瀏覽網站只會用到 GETHEAD 這兩種請求方法,登入或送出表單會再用到 POST,其餘像 PUTDELETETRACE 這類方法,正常訪客幾乎用不到,出現的話多半是攻擊工具在探測伺服器的弱點。直接擋掉這些方法,能過濾掉一部分自動化攻擊流量:

RewriteCond %{REQUEST_METHOD} !^(GET|HEAD|POST|OPTIONS)$
RewriteRule .* - [F]Code language: plaintext (plaintext)

這段規則的邏輯是先判斷請求方法是不是落在允許的清單裡,只要不是 GETHEADPOSTOPTIONS 其中之一,就直接回傳拒絕,不讓請求往下處理。

封鎖或放行 AI 訓練爬蟲,各有得失

目前比較常見的 AI 爬蟲各有自己的名字與用途,GPTBot 是 OpenAI 用來抓取內容訓練模型的爬蟲,ClaudeBot 是 Anthropic 的,CCBot 是 Common Crawl 這個公開資料集計畫的爬蟲,Bytespider 是字節跳動的,PerplexityBot 則是 Perplexity 在即時回答使用者問題時,用來抓取相關內容的爬蟲。

要不要擋,其實不是非黑即白的單一答案,比較像一個策略選擇。如果站主希望自己的內容能被 ChatGPT、Perplexity 這類工具在回答裡直接引用,也就是近年常被談到的生成式引擎優化,通常會保留像 PerplexityBot 這種即時查詢型的爬蟲讓它放行;如果只是單純不想讓內容整段被拿去訓練模型,又不太在意會不會被 AI 搜尋引用,才會連 GPTBot 這類訓練型爬蟲一起擋掉。實際擋法是判斷 User-Agent 字串裡有沒有出現這些爬蟲的名字:

RewriteCond %{HTTP_USER_AGENT} (GPTBot|ClaudeBot|CCBot|Bytespider) [NC]
RewriteRule .* - [F,L]Code language: plaintext (plaintext)

清單裡列的只是幾個常見的訓練型爬蟲,實際要擋哪幾個、留哪幾個,依自己的策略調整清單內容即可。

常見 AI 爬蟲分成訓練型與即時查詢型,想避免內容被拿去訓練就擋、想被 AI 引用就放行
GPTBot、ClaudeBot 這類訓練型爬蟲多半會擋,PerplexityBot 這種即時查詢型則常保留給想被 AI 引用的內容。

wp-config.php 與敏感檔案不該被外部讀到

wp-config.php 這個檔案裡存著資料庫帳號密碼、資料表前綴,還有一整組安全金鑰,是整個網站最不該被外部直接讀到的檔案,一旦外流,等於把整個網站的鑰匙雙手奉上。同一個邏輯延伸到另外兩個容易被忽略的角落:上傳資料夾裡不該能執行 PHP,避免駭客把後門程式偽裝成圖片上傳後直接觸發執行;wp-includes 底下也有些檔案,本來就只給 WordPress 核心自己引用,不該被外部瀏覽器直接開啟。這三個子點都會沿用前面「Apache 存取控制語法」那節介紹過的新語法來寫。

wp-config.php 直接擋掉外部讀取

這個檔案的重要性再怎麼強調都不為過,直接用新版 Apache 的語法擋掉外部存取:

<Files wp-config.php>
Require all denied
</Files>Code language: plaintext (plaintext)

這段規則的意思很單純,任何人試圖直接用網址存取 wp-config.php,伺服器一律拒絕,不管請求來自哪裡。

uploads 資料夾關閉 PHP 執行權限

這是很多入門教學會漏掉的一步,卻是防止後門攻擊最有效的手段之一。上傳資料夾本來就只該放圖片、影片這類媒體檔,關掉裡面的 PHP 執行權限完全不影響正常使用,卻能一次擋掉一整類攻擊。做法是在 wp-content/uploads/ 這個目錄底下,另外新建一個 .htaccess,跟根目錄那一份是完全不同的檔案,內容只需要:

<Files *.php>
Require all denied
</Files>Code language: plaintext (plaintext)

這樣一來,就算有人把惡意 PHP 檔案偽裝成圖片檔名,成功上傳進這個資料夾,伺服器也會拒絕執行它,那個檔案只能被當成一般的靜態檔案下載,沒辦法被觸發執行,等於在攻擊鏈的最後一步就被擋了下來。

wp-includes 底下擋掉不該公開的檔案

wp-admin/includes 底下的檔案、wp-includes 底下大部分的 PHP 檔案、TinyMCE 的語言檔,還有 theme-compat 資料夾,這幾類本來就只給 WordPress 核心內部呼叫,一般讀者的瀏覽器請求用不到,也不該被外部直接開啟,可以用 mod_rewrite 直接擋掉:

RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]Code language: plaintext (plaintext)

這組規則沿用 WordPress 社群長期使用的寫法,如今收錄在 WordPress.org 官方文件的「Hardening WordPress」頁面裡,[F,L] 這個旗標本身沒有 Apache 2.2 跟 2.4 的語法差異,不受前面 Require 與 Order 新舊語法差異的影響。有一個例外要特別留意,這組規則在 WordPress Multisite 這種多站點網路環境下不建議整包照搬,因為其中部分規則會連帶擋到 Multisite 用來產生圖片的 ms-files.php,導致某些圖片顯示不出來。經營 Multisite 的站主要嘛跳過這一段,要嘛自行調整,把 ms-files.php 排除在規則之外。

防盜連,擋掉別人網站直接呼叫你的圖片

盜連指的是別人網站的頁面,直接引用你網站上的圖片網址,讓那張圖顯示在別人的頁面上,圖片跑的流量與頻寬卻是由你的網站在付。要判斷一個請求算不算盜連,靠的是 HTTP_REFERER 這個欄位,也就是訪客是從哪一個網頁點過來的;空白的 referer 代表沒有上一頁資訊,可能是訪客直接貼網址開啟,也可能是某些 RSS 閱讀器抓取的行為,這種情況要放行,不是自家網域發出的請求才擋:

RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?你的網域\.com/ [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp)$ - [F,L]Code language: plaintext (plaintext)

這段規則先判斷 referer 不是空白、也不是自家網域,兩個條件都成立才會觸發封鎖。擋掉之後,可以選擇直接回傳 403 拒絕存取,或者導向一張替代圖片,提醒對方這是被盜連的圖,只要把最後一行換成:

RewriteRule \.(jpg|jpeg|png|gif|webp)$ https://你的網域.com/warning.png [NC,R,L]Code language: plaintext (plaintext)

設定盜連規則前,有一件事一定要記得,社群平台的分享預覽功能,例如 Facebook、LINE 抓取縮圖顯示在分享卡片上,同樣會經過這段 referer 檢查。網域清單裡要把這些服務一起放行,不然文章分享出去,卡片上會看不到縮圖,反而影響到正常的社群曝光。

Gzip 壓縮與瀏覽器快取標頭加快頁面載入

文字類的內容,像 HTML、CSS、JavaScript,先壓縮再送出,能明顯縮小傳輸量;圖片、樣式表這類不常變動的靜態資源,讓瀏覽器直接把它們快取起來,下一次造訪就不用重新下載,兩者都是讓網頁載入速度變快的基本功。

不過在動手設定之前,先確認一件事,如果網站已經裝了 WP Rocket、LiteSpeed Cache 這類頁面快取外掛,或者主機本身是 LiteSpeed、又或者掛在 Cloudflare 這類服務後面,壓縮與快取標頭多半已經由外掛或主機層級處理過了,這時候再重複設定不一定有效果,甚至可能跟既有機制互相衝突。先確認現有環境有沒有在管這一塊,再決定要不要自己動手寫規則。

mod_deflate 先壓縮文字內容再送出

mod_deflate 負責壓縮文字類型的內容,只鎖定 HTML、CSS、JavaScript 這幾種類型就夠了,圖片本身通常已經是壓縮格式,重複壓縮意義不大,反而多耗一次運算資源:

AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascriptCode language: plaintext (plaintext)

有一件事 Apache 官方文件本身就特別提醒,透過 TLS,也就是 https,連線傳送經過 deflate 壓縮的內容,某些網頁應用程式容易受到一類統稱 BREACH 的資訊揭露攻擊,這不是危言聳聽,而是官方文件寫在說明裡的注意事項,設定前值得留意自己的網站架構會不會落在這個風險範圍內。

mod_expires 設定圖片與樣式表的快取期限

mod_expires 負責設定 ExpiresCache-Control 這兩個回應標頭,告訴瀏覽器某個檔案可以快取多久,起算方式有兩種可以選,access 是從訪客存取的當下開始起算,適合圖片這類不常變動、又會被同一個人短時間內反覆瀏覽的資源;modification 則是從檔案最後修改時間開始起算,適合像每週更新一次、固定網址不變的公告頁面。用 modification 的話,同一份文件在所有訪客的快取裡會同時到期;用 access 的話,每個訪客各自的到期時間不一樣,比較適合圖片這種會被重複瀏覽的資源。

實際設定可以參考下面這段完整片段:

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresDefault "access plus 2 days"
</IfModule>Code language: plaintext (plaintext)

圖片格式設定長達 1 年的快取期限,因為圖片一旦發布很少會再改動;CSS 跟 JavaScript 設定 1 個月,留一點更新的彈性;沒有特別列出類型的檔案,則統一用 2 天當預設值。

Nginx 主機不會讀取 .htaccess 裡的規則

上面每一段規則,用的都是 Apache 專屬的語法,只有在 Apache,或者相容 mod_rewrite 的環境下才會生效,這是動手前最後、也最重要的一個提醒。

Apache 的設計是每一次請求進來,都重新讀取當下目錄裡的 .htaccess 檔案,好處是隨時能改規則,不需要重啟伺服器,代價是每個請求都要多做一次檔案系統的讀取,速度上會慢一些。Nginx 的設計邏輯完全不同,所有規則都寫進一次性載入的伺服器設定檔,也就是 server 區塊與 location 區塊,伺服器啟動時讀一次之後就常駐在記憶體裡,效能因此比較好,代價是不支援 Apache 那種讓每個目錄各自放一份規則檔的彈性。

也因為這樣的架構差異,WordPress.org 官方支援論壇也明確指出,Nginx 是不會使用 .htaccess 檔案的網頁伺服器,就算把 .htaccess 檔案原封不動搬上去,Nginx 完全不會去讀取它,規則不會生效,卻也不會跳出任何錯誤訊息,這是最容易讓人誤以為「規則沒寫對」的地方,實際上是這個檔案根本沒有被執行過。想確認自己的主機是哪一種,最直接的方法是問主機商,或者觀察現有的固定網址規則有沒有實際生效,如果訪問一篇文章的固定網址正常顯示內容,而不是跳出 404,通常代表主機在跑 Apache 或相容環境。

如果確認自己的主機是 Nginx,前面每一段規則的概念都能對應到 Nginx 自己的語法,轉址在 Nginx 用 return 301,限制存取用 denyallow 指令,快取標頭用 add_headerexpires 指令,語法寫法不同,背後的概念完全一致。如果不是自己直接管理伺服器,而是用主機商代管的 Nginx 環境,這些設定通常得透過主機商提供的控制台介面調整,或者直接請主機商的技術支援協助處理,不是靠自己上傳一個 .htaccess 就能解決。

.htaccess 是少數只靠一個文字檔,就能同時管到安全與效能的地方,正因為它的影響範圍是整個網站,動手的順序比規則本身更重要,先備份,一次只加一段,改完就馬上驗證有沒有正常運作。把這幾個習慣做好,這張貼在網站大門口的規則表,才會真的照著你想要的方式運作,而不是變成下一次半夜跳出白畫面的原因。

常見問答

本區問答由 AI 依文章內容自動整理,僅供快速參考,正式內容仍以全文為準。

.htaccess 設定被改壞,網站會發生什麼事?

輕則某一段規則不生效,重則整個網站直接跳出 500 Internal Server Error 或一片空白。動手前一定要先備份,一次只加一段測試,出錯就把備份傳回去覆蓋,或直接刪除檔案,讓 WordPress 自動重建最基本的預設規則。

轉址要用 301 還是 302?

要用 301。301 代表永久轉址,搜尋引擎會把舊網址原本累積的排名權重轉移到新網址上;302 代表暫時轉址,搜尋引擎不會轉移權重,舊網址的排名累積很可能就這樣白白流失。換網址時該用哪一種,答案永遠是 301。

.htaccess 怎麼統一 http、www 網址?

用 mod_rewrite 判斷 HTTPS 是否關閉、主機名稱是不是以 www 開頭這兩個條件,合併進同一組規則,用 [OR] 串起來,只要中一個條件就一次性導到選定的最終網址版本,不需要分兩段轉址,能少一次來回的延遲。

.htaccess 能封鎖 GPTBot 這類爬蟲嗎?

可以。robots.txt 只是君子協定沒有強制力,要擋得在 .htaccess 判斷 User-Agent 字串裡有沒有出現 GPTBot、ClaudeBot,直接回傳拒絕。想被 AI 引用,常保留查詢型的 PerplexityBot。

wp-config.php 需要額外設定保護嗎?

需要。wp-config.php 存著資料庫帳密與安全金鑰,一旦外流等於把整個網站的鑰匙雙手奉上,必須用 Require all denied 直接擋掉外部讀取,這是保護敏感檔案最基本的一步,上傳資料夾與核心程式檔案也適用同樣邏輯。

資料來源
  1. Access Control - Apache HTTP Server Version 2.4 — Apache HTTP Server
  2. mod_deflate - Apache HTTP Server Version 2.4 — Apache HTTP Server
  3. Hardening WordPress — WordPress.org
  4. NGINX web server, which does not utilize .htaccess files — WordPress.org