2026年11月12日

2026年11月12日

隱藏 WordPress 版本資訊的方法|generator 標籤、ver=、readme.html

本文是日文原文的在地化版本。 閱讀日文原文

WordPress 在初始狀態下,會把自己的版本號碼寫進頁面的 HTML 與 feed 中。攻擊者可能會看這個資訊,作為判斷是否為有已知漏洞版本的線索。本文會確認版本顯示在哪些地方,並說明隱藏的步驟。內容為 2026 年 10 月時的資訊。

首先希望你知道的是,隱藏版本只是輔助性的對策。因為也有從 CSS 或 JavaScript 檔案的內容等推測版本的方法,所以即使隱藏,也不一定能防止被辨識出來。最有效的是讓 WordPress 核心、外掛、佈景主題保持在最新版本。

版本會顯示的地方

位置 顯示的內容 對策
HTML 的 <head> 像 <meta name="generator" content="WordPress 6.9.4"> 這樣的標籤 the_generator 篩選器
RSS、Atom feed 像 <generator>https://wordpress.org/?v=6.9.4</generator> 這樣的行 同樣使用 the_generator 篩選器
CSS、JavaScript 的 URL 像 style.min.css?ver=6.9.4 這樣的 ?ver= style_loader_src、script_loader_src 篩選器
HTTP 標頭 X-Powered-By: PHP/8.3.x(不是 WordPress 而是 PHP 的版本) php.ini 的 expose_php
readme.html 會顯示出使用 WordPress(目前的 readme.html 裡沒有寫版本號碼) 拒絕存取

REST API 的 /wp-json/ 回傳的網站資訊(名稱、說明、URL、時區、命名空間等)中,不包含 WordPress 的版本。不需要為了隱藏版本而刪減 REST API 的輸出。

步驟 1:確認目前的顯示狀態

請換成自己網站的 URL,在自己的電腦或伺服器上執行。如果使用快取類外掛或 CDN,變更後請先清除快取再確認。

# HTML 的 generator 標籤
curl -s https://example.com/ | grep -i 'name="generator"'

# feed 的 generator
curl -s https://example.com/feed/ | grep -i '<generator>'

# 列出 CSS、JS 上附帶的 ver= 的值
curl -s https://example.com/ | grep -oE 'ver=[0-9][0-9.]*' | sort | uniq -c

# HTTP 標頭的 X-Powered-By
curl -sI https://example.com/ | grep -i 'x-powered-by'

# readme.html 是否可以讀取(200 代表可以讀取)
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/readme.html

如果 ver= 的清單中,有和後台「儀表板」(Dashboard)→「更新」(Updates)所顯示的 WordPress 版本相同的值,就表示可以從那些 CSS 或 JS 得知 WordPress 的版本。WordPress 核心或佈景主題載入 CSS、JS 時如果沒有指定版本,WordPress 的版本就會被放進 ver=。

步驟 2:決定程式碼要放的位置

本文的程式碼,請放在下列任一處。

  • mu-plugin(建議):在 wp-content/mu-plugins/ 資料夾(沒有的話就建立)中放入 PHP 檔案,不需要啟用操作就會被載入。更換佈景主題後效果仍然持續
  • 子佈景主題的 functions.php:如果寫在父佈景主題的 functions.php,會在佈景主題更新時消失

如果採用 mu-plugin,請用像 wp-content/mu-plugins/hide-wp-version.php 這樣的名稱,把下一個步驟的程式碼整合成一個檔案儲存。

步驟 3:隱藏 generator 標籤與 ver= 的版本

<?php
/**
 * Plugin Name: Hide WordPress Version
 * Description: 隱藏 generator 標籤,以及 CSS、JS 的 ver= 中出現的 WordPress 版本。
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// 將 HTML 的 <meta name="generator"> 與 feed 的 <generator> 清空。
add_filter( 'the_generator', '__return_empty_string' );

/**
 * 只有在 CSS、JS 的 ver= 與 WordPress 的版本相同時,才替換成其他的值。
 * 因為是替換而不是移除 ver=,所以 WordPress 更新之後,瀏覽器的快取也會更新。
 */
function wpdr_replace_wp_version_query( $src ) {
	if ( ! is_string( $src ) || '' === $src ) {
		return $src;
	}

	$query = wp_parse_url( $src, PHP_URL_QUERY );
	if ( empty( $query ) ) {
		return $src;
	}

	parse_str( $query, $args );
	$wp_version = get_bloginfo( 'version' );

	if ( isset( $args['ver'] ) && $args['ver'] === $wp_version ) {
		$src = add_query_arg( 'ver', substr( wp_hash( $wp_version ), 0, 10 ), $src );
	}

	return $src;
}
add_filter( 'style_loader_src', 'wpdr_replace_wp_version_query', 20 );
add_filter( 'script_loader_src', 'wpdr_replace_wp_version_query', 20 );

重點如下。

  • the_generator 篩選器,對 HTML 的 generator 標籤與 feed 的 generator 兩者都有效。remove_action( 'wp_head', 'wp_generator' ); 只會移除 HTML 的標籤,所以對 feed 沒有效果
  • 如果把 ?ver= 全部移除,在更新外掛或佈景主題時,瀏覽器可能會繼續使用舊的 CSS、JS。上面的程式碼只在與 WordPress 版本相同的值時,才替換成由版本產生的另一個字串。WordPress 更新後值也會改變,所以快取會被更新
  • 外掛或佈景主題自行附加版本號碼的 ver=,不會被更動

儲存之後,請用步驟 1 的指令確認 generator 不再出現,以及 ver= 的清單中沒有殘留 WordPress 的版本。如果使用快取類外掛,請先清除快取再確認。

步驟 4:停用 X-Powered-By 標頭

X-Powered-By 是由 PHP 而不是 WordPress 所附加的標頭,可以從中得知 PHP 的版本。要停用的話,請在 PHP 的設定檔(php.ini)中將 expose_php 設為 Off。這個設定只能在 php.ini 中變更。

expose_php = Off

這是在 VPS 的 Ubuntu 上使用 PHP-FPM 時的範例(8.3 請配合所使用的 PHP 版本)。

# 確認目前的設定
grep -n '^expose_php' /etc/php/8.3/fpm/php.ini

# 改寫之後重新啟動 PHP-FPM
sudo systemctl restart php8.3-fpm

Ubuntu 套件的 php.ini,初始狀態就是 expose_php = Off。如果標頭仍然出現,請確認是否正在使用另一個 php.ini。在日本的租用伺服器上,有些可以在後台的 PHP 設定中變更,有些則無法變更。

無法變更 php.ini 時,也有用 WordPress 程式碼移除標頭的方法。不過 send_headers 是 WordPress 輸出前台頁面時才會執行的掛鉤,所以對後台或登入頁面沒有效果。

add_action( 'send_headers', function () {
	header_remove( 'X-Powered-By' );
} );

步驟 5:拒絕存取 readme.html 與 license.txt

readme.html 與 license.txt 是 WordPress 核心的檔案。目前的 readme.html 中沒有寫版本號碼,但可以看出使用了 WordPress。

即使刪除,更新 WordPress 核心之後也會恢復。與其刪除,不如在網頁伺服器上拒絕存取,比較省事。

Apache(.htaccess)的情況

加到 WordPress 資料夾中的 .htaccess。# BEGIN WordPress 與 # END WordPress 之間會被 WordPress 改寫,所以請寫在這個範圍之外。這是 Apache 2.4 的寫法(Order、Deny from all 是 Apache 2.2 的舊寫法)。

<FilesMatch "^(readme\.html|license\.txt)$">
    Require all denied
</FilesMatch>

Nginx 的情況

加到網站的 server 區塊,確認設定後重新載入。

location ~* ^/(readme\.html|license\.txt)$ {
    deny all;
}
sudo nginx -t && sudo systemctl reload nginx

加入之後執行 curl -s -o /dev/null -w '%{http_code}\n' https://example.com/readme.html,確認會回傳 403。

隱藏仍無法防範的事

  • WordPress 的 CSS、JS 檔案內容,每個版本都有些許不同,所以只要取得檔案來比對,就能推測出版本
  • 從外掛或佈景主題的 readme.txt 或 ver=,有時可以得知外掛的版本。容易成為目標的是外掛的漏洞,而不是核心
  • 能夠登入後台的人,仍然會和以前一樣看到版本

因此,在隱藏版本的同時,也請進行核心、外掛、佈景主題的更新,刪除不使用的外掛,以及停用後台的檔案編輯等。

總結

  • 版本會出現的主要位置,是 HTML 與 feed 的 generator,以及 CSS、JS 的 ?ver=。REST API 的 /wp-json/ 不包含 WordPress 的版本
  • 用 add_filter( 'the_generator', '__return_empty_string' ); 就能同時清除 HTML 與 feed 的 generator
  • ?ver= 不要移除,只替換與 WordPress 版本相同的值,就不會妨礙快取的更新
  • X-Powered-By 以 php.ini 的 expose_php = Off 停用
  • readme.html 即使刪除也會因更新而恢復,所以改在網頁伺服器上拒絕存取
  • 隱藏只是輔助性的對策,持續更新才是最重要的

相關文章:

對 WordPress 有疑問嗎?本站營運方位於日本,僅以日文回覆。 聯絡我們(日文)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲