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 订阅源 | 形如 <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"'
# 订阅源的 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(推荐):把 PHP 文件放进
wp-content/mu-plugins/文件夹(没有的话就创建),无需启用操作就会被加载。更换主题后效果依然持续 - 子主题的 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"> 和订阅源的 <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 标签和订阅源的 generator 都有效。remove_action( 'wp_head', 'wp_generator' );只会去掉 HTML 的标签,对订阅源无效- 如果把
?ver=全部删掉,在更新插件或主题时,浏览器可能会继续使用旧的 CSS、JS。上面的代码只在与 WordPress 版本相同的值时,才替换成由版本生成的另一个字符串。WordPress 更新后这个值也会变,所以缓存会随之更新 - 插件或主题自己附加了版本号的
ver=不会被更改
保存之后,用步骤 1 的命令确认 generator 不再出现,以及 ver= 的列表中没有残留 WordPress 的版本。如果使用了缓存类插件,请先清除缓存再确认。
步骤 4:去掉 X-Powered-By 头
X-Powered-By 不是 WordPress,而是 PHP 添加的头,可以看出 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 核心之后它们也会恢复。与其删除,不如在 Web 服务器中拒绝访问,更省事。
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 和订阅源的 generator,以及 CSS、JS 的
?ver=。REST API 的/wp-json/中不包含 WordPress 的版本 - 用
add_filter( 'the_generator', '__return_empty_string' );可以同时去掉 HTML 和订阅源的 generator ?ver=不要删除,只替换与 WordPress 版本相同的值,就不会妨碍缓存的更新X-Powered-By用 php.ini 的expose_php = Off去掉readme.html即使删除也会在更新后恢复,所以在 Web 服务器中拒绝访问- 隐藏只是辅助性的措施,持续更新才是最重要的
相关文章:
- WordPress 安全头的设置方法|.htaccess、Nginx、functions.php 各自的做法
- 如何限制对 WordPress REST API 的访问
- 停用 WordPress 主题和插件文件编辑器|DISALLOW_FILE_EDIT
关于 WordPress 有疑问?本站运营方位于日本,仅以日语回复。 联系我们(日语)