0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

WordPress 防垃圾留言總整理:外掛、留言設定、蜜罐與字數限制

xampp或php啟用Zend Optimizer-windows版本

WordPress 網站開放留言之後,很快就會收到一堆看不懂的英文、日文留言,內容多半是廣告連結。這些垃圾留言不只讓後台很難整理,萬一不小心被核准公開,訪客也會覺得這個網站沒人在管。

擋垃圾留言沒有一招就搞定的方法,比較好的做法是一層一層擋:先用 WordPress 內建的設定,再裝一個防垃圾留言外掛,用不到留言的地方直接關掉,最後才視需要加上程式碼。這篇就照這個順序整理。

(原本另外三篇〈用代碼實現 wordpress 評論留言數字驗證〉、〈wordpress 使用 .htaccess 有效的杜絕垃圾評論〉、〈用代碼設定 wordpress 留言評價的最大字數及最小字數〉的內容,已經整合到這裡。)

第一層:調整「設定 → 討論」

到後台「設定 → 討論」,下面這幾個選項不用裝任何東西,就能擋掉大部分的垃圾留言(選項文字依 WordPress 版本會有些不同):

  • 留言者必須填寫姓名及電子郵件地址:建議勾選。
  • 留言必須經過人工核准,或是留言者先前必須有一則已核准的留言:勾選後,新面孔的留言要你按「核准」才會公開,垃圾留言就算送進來,也不會出現在網站上。
  • 自動關閉舊文章的留言:垃圾留言最常鎖定發佈很久的舊文章,設定發佈超過 30~90 天後自動關閉,對真正的讀者影響很小。
  • 留言裡的連結數量:預設包含 2 個以上連結的留言會先進入審核,可以改成 1,只要有連結就先擋下來給你看。
  • 留言審核關鍵字:留言的內容、名稱、網址、電子郵件或 IP 符合清單裡的字,就會先進入審核。
  • 不允許使用的關鍵字(舊版叫「留言黑名單」):符合清單的留言會直接被移到垃圾桶。

兩個關鍵字清單都是一行填一個字或一個 IP。要注意它會比對「字的一部分」,例如填 press 也會擋到 WordPress,所以不要填太短、太常見的字。

第二層:安裝防垃圾留言外掛

設定只能擋掉比較明顯的垃圾留言,大量自動送出的廣告留言,交給外掛判斷比較省事。下面兩個擇一即可:

  • Akismet:WordPress.com 的母公司 Automattic 推出,下載 WordPress 時就內附這個外掛,啟用後才會作用。留言送出時會交給 Akismet 的伺服器判斷,是垃圾留言就自動移到「垃圾留言」。要到官網申請金鑰,個人、非商業網站可以免費使用,公司或商業網站依官方規定要選付費方案。因為留言資料會送到國外的伺服器,記得在網站的隱私權政策裡說明。
  • Antispam Bee:免費、不用申請帳號,主要在自己的主機上判斷,例如用蜜罐欄位、檢查留言是不是填得太快,比較沒有資料外送的顧慮。

誤判的時候,到「留言」頁面的「垃圾留言」分類裡找到那則留言,把它標記為不是垃圾留言,就能救回來。

如果垃圾留言真的多到擋不完,也可以加上 Cloudflare Turnstile 或 Google reCAPTCHA 這類人機驗證(外掛目錄搜尋就有相關外掛),代價是真正的讀者留言時會多一個步驟。

第三層:用不到留言的地方,直接關掉

公司形象網站的頁面、媒體附件頁,本來就不需要留言,開著只會招來垃圾留言。關閉的方法請看WordPress 關閉留言功能。

另外,Pingback、Trackback 這兩個舊功能,現在收到的幾乎都是垃圾通知,也建議一起關閉,做法請看WordPress 關閉 Pingback 與 Trackback。

第四層:用程式碼加強

下面的程式碼都是加到子主題的 functions.php,或用 Code Snippets 外掛新增,不要直接改主題本身的檔案(原因請看WordPress functions.php 實用程式碼整理)。每加一段,都用無痕視窗(沒有登入)實際留言一次,確認真人還能正常留言。

1. 蜜罐欄位:讓機器人自己露出馬腳

「蜜罐」(Honeypot)是在留言表單裡多放一個真人看不到的欄位。真人不會填它,但自動送留言的程式通常會把每個欄位都填滿,只要這個欄位有值,就知道是機器送的。

它也能順便擋掉「根本沒打開網頁、直接對 wp-comments-post.php 送資料」的程式,因為這種送法不會帶著這個欄位。

PHP
// 1) 在留言表單裡加一個真人看不到的欄位
add_action( 'comment_form', function () {
    echo '<p class="ahwei-hp" aria-hidden="true" style="position:absolute;left:-9999px;">'
        . '<label for="ahwei-hp">這個欄位請保持空白</label>'
        . '<input type="text" id="ahwei-hp" name="ahwei_hp" value="" tabindex="-1" autocomplete="off">'
        . '</p>';
} );

// 2) 送出留言時檢查:沒有這個欄位(沒經過表單)或欄位被填了,就是機器
add_action( 'pre_comment_on_post', function () {
    if ( is_user_logged_in() ) {
        return;
    }
    if ( ! isset( $_POST['ahwei_hp'] ) || '' !== $_POST['ahwei_hp'] ) {
        wp_die(
            '留言送出失敗,請回上一頁重新整理後再試一次。',
            '留言送出失敗',
            array( 'response' => 403, 'back_link' => true )
        );
    }
} );
  • 欄位用 CSS 移到畫面外;tabindex="-1" 和 aria-hidden 是讓用鍵盤操作、用螢幕報讀軟體的真人,也不會跑到這個欄位。
  • 如果你的主題留言表單是自己寫的、沒有呼叫 comment_form 這個 hook,欄位就不會出現,所有訪客的留言都會被擋。所以加好之後一定要實際測試。

2. 限制留言的最少、最多字數

太短的「Nice post!」、太長的一整篇廣告文,都是垃圾留言的特徵。

原本文章用 strlen() 計算長度,但它算的是「位元組」而不是字數。一個中文字在 UTF-8 裡佔 3 個位元組,設定「最少 60 個字元」其實 20 個中文字就達標,跟原本寫的「約 30 個中文字」對不起來。改用 mb_strlen() 算的才是真正的字數:

PHP
add_filter( 'preprocess_comment', function ( $commentdata ) {
    // 只檢查一般留言;管理員、編輯在後台回覆不受限制
    $type = isset( $commentdata['comment_type'] ) ? $commentdata['comment_type'] : '';
    if ( ! in_array( $type, array( '', 'comment' ), true ) || current_user_can( 'moderate_comments' ) ) {
        return $commentdata;
    }

    $min    = 5;    // 最少幾個字
    $max    = 1000; // 最多幾個字
    $text   = trim( wp_strip_all_tags( $commentdata['comment_content'] ) );
    $length = mb_strlen( $text, 'UTF-8' );

    if ( $length < $min ) {
        wp_die(
            sprintf( '留言太短了,最少要 %d 個字喔!', $min ),
            '留言太短',
            array( 'response' => 400, 'back_link' => true )
        );
    }
    if ( $length > $max ) {
        wp_die(
            sprintf( '留言太長了,最多 %d 個字。', $max ),
            '留言太長',
            array( 'response' => 400, 'back_link' => true )
        );
    }

    return $commentdata;
} );
  • $min、$max 改成你要的字數。最少字數不要設太高,「謝謝分享!」只有 5 個字,設成 30 字會把真心但簡短的留言也擋掉。
  • 有審核留言權限的管理員、編輯不受限制,在後台回覆留言不會被擋。
  • 只檢查一般留言,不影響 Pingback、Trackback。

3. 只收中文留言(適合讀者都在台灣的網站)

原本文章的第二種方法,是把沒有任何中文字、或含有日文假名的留言視為垃圾留言。但原本的程式碼呼叫了一個不存在的 err() 函式,在新版 PHP 會直接出錯。下面是修正後的寫法,而且改成丟進「垃圾留言」而不是直接擋掉,萬一誤判還能救回來:

PHP
add_filter( 'pre_comment_approved', function ( $approved, $commentdata ) {
    // 已經被判定為垃圾留言的,就不用再檢查
    if ( is_wp_error( $approved ) || in_array( $approved, array( 'spam', 'trash' ), true ) ) {
        return $approved;
    }
    // 管理員、編輯的留言不檢查
    if ( current_user_can( 'moderate_comments' ) ) {
        return $approved;
    }
    // 只檢查一般留言
    $type = isset( $commentdata['comment_type'] ) ? $commentdata['comment_type'] : '';
    if ( ! in_array( $type, array( '', 'comment' ), true ) ) {
        return $approved;
    }

    $text        = $commentdata['comment_content'];
    $has_chinese = preg_match( '/\p{Han}/u', $text );
    $has_kana    = preg_match( '/[\x{3040}-\x{30FF}]/u', $text );

    if ( ! $has_chinese || $has_kana ) {
        return 'spam';
    }
    return $approved;
}, 10, 2 );
  • \p{Han} 代表中文字(漢字),\x{3040}-\x{30FF} 是日文的平假名與片假名。日文留言裡也會有漢字,所以要另外檢查假名。
  • 網站有外國讀者、或常有人用英文留言的話,就不要用這段。

用 .htaccess 擋「直接送出」的留言:有用,但有風險

原本另一篇文章的做法,是在 .htaccess 裡檢查送往 wp-comments-post.php 的留言:如果不是從自己網站的網頁送出(看瀏覽器帶的 Referer),或是沒有瀏覽器資訊(User-Agent),就擋掉。舊寫法的網域比對太寬鬆,擋下來之後還會把對方轉到一個奇怪的網址。修正後的寫法如下,放在 # BEGIN WordPress 的上面,example\.com 換成自己的網域:

Apache (.htaccess)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} wp-comments-post\.php [NC]
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com/ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule ^ - [F]
</IfModule>

意思是:送出留言時,如果來源不是自己的網站,或沒有瀏覽器資訊,就回應 403 拒絕。

但這個方法有幾個限制與風險,使用前要想清楚:

  • 擋不住聰明一點的程式:Referer 和 User-Agent 都是送出的一方自己填的,要偽造非常容易,擋得到的只有最簡陋的機器人。
  • 可能誤擋真人:有些瀏覽器的隱私設定、擴充功能或公司防火牆會把 Referer 拿掉,這些訪客留言時會直接看到 403 錯誤,而你完全不會知道。
  • 網址改了要記得改:網站換網域時忘了同步修改,所有人都會無法留言。
  • 只適用 Apache、LiteSpeed 主機,Nginx 主機不讀 .htaccess。

上面的蜜罐欄位一樣能擋掉「沒經過表單」的留言,又不會誤擋把 Referer 拿掉的真人,建議優先用蜜罐。.htaccess 的其他常用設定,請看.htaccess 用法大全。

舊版文章裡不建議再用的做法

  • 在 .htaccess 一個一個封鎖 IP:舊寫法 Order Allow,Deny、Deny from 是舊版 Apache 的語法,建議改用 Require。更大的問題是垃圾留言的 IP 幾乎每次都不一樣,一個一個封效果很有限。真的要封,把 IP 填進「討論」設定的關鍵字清單最簡單。
  • 自己寫程式比對特定關鍵字:舊程式碼一樣用了不存在的 err(),還要把記錄寫進主機的系統資料夾,一般虛擬主機沒有這個權限。關鍵字過濾請直接用「討論」設定裡的清單。
  • 自製數字驗證碼、加法驗證(原本〈數字驗證〉那篇):舊寫法把正確答案、或是要相加的兩個數字,放在表單的隱藏欄位裡一起送出,機器人只要讀取網頁就能拿到答案,等於沒擋。需要人機驗證的話,請改用前面提到的 Turnstile、reCAPTCHA 外掛。

延伸閱讀

垃圾留言擋得好,後台才不會每天被廣告淹沒。正威網路行銷是 25 年經驗的基隆網頁設計工作室,也提供網站維護託管,網站的日常更新與安全設定不想自己顧的話,歡迎來聊聊。

分享這篇文章

返回文章列表更多「Wordpress代碼教學」文章

發表留言

還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。

讓我們一起打造
您的專屬網站!