本文约 6,640 字,预计阅读 17 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:登录页是站点最该收口的地方
/wp-login.php 是全网被扫描最多的路径之一。任何一台自动化扫描器,发现 WordPress 站点后第一件事就是 POST 到这个地址做撞库。
所以登录页有两个层次的需求:
- 品牌化:客户看到千篇一律的 WordPress Logo 会觉得不专业,换成自己的 Logo 和配色;
- 安全收口:隐藏或保护登录入口,降低被爆破的概率。
这两件事都能用代码实现,不需要装插件(装插件反而多一个攻击面)。
原理分析:登录页为什么可以被改
wp-login.php 输出时提供了丰富的钩子:
login_head—— 在<head>里输出内容;login_headerurl—— Logo 的链接地址(默认指向 wordpress.org);login_headertext—— Logo 的 title 属性;login_enqueue_scripts—— 加载自定义 CSS/JS;login_message—— 登录表单上方的提示文字;login_redirect—— 登录后跳转地址;login_footer—— 页脚区域。
默认 Logo 是 CSS 背景图,来自 wp-admin/images/w-logo-blue.png(部分版本是 SVG)。所以”改 Logo”本质是覆盖 CSS 的 background-image。
注意一个技术细节:这些钩子只在 wp-login.php 页面触发,所以在主题 functions.php 里写是可以的,但登录页不会加载主题的常规 CSS。登录页加载的是 wp-admin/css/login.css(核心样式)+ 你通过钩子加的样式。
保姆级实操
一、改 Logo 和链接
把下面代码放进子主题 functions.php(或自建功能插件):
// 改 Logo 图片(建议用站点 logo,尺寸 84x84 左右,透明底 PNG)
add_action( 'login_enqueue_scripts', function() { ?>
<style type="text/css">
#login h1 a, .login h1 a {
background-image: url(<?php echo esc_url( get_stylesheet_directory_uri() . '/assets/images/login-logo.png' ); ?>);
background-size: contain;
background-position: center;
width: 180px;
height: 84px;
background-repeat: no-repeat;
padding-bottom: 20px;
}
body.login {
background: #f4f6f8;
}
.login form {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,.06);
}
.wp-core-ui .button-primary {
background: #c0392b;
border-color: #c0392b;
}
.wp-core-ui .button-primary:hover {
background: #a5301f;
border-color: #a5301f;
}
.login #backtoblog a, .login #nav a {
color: #555;
}
</style>
<?php } );
// Logo 链接指向站点首页
add_filter( 'login_headerurl', function( $url ) {
return home_url();
} );
// Logo 的 title 文字
add_filter( 'login_headertext', function( $text ) {
return get_bloginfo( 'name' );
} );
二、登录后按角色跳转到不同页面
add_filter( 'login_redirect', function( $redirect_to, $request, $user ) {
if ( isset( $user->roles ) && is_array( $user->roles ) ) {
if ( in_array( 'administrator', $user->roles, true ) ) {
return admin_url(); // 管理员进后台
}
if ( in_array( 'subscriber', $user->roles, true ) ) {
return home_url( '/member/' ); // 订阅者进会员中心
}
return home_url( '/account/' ); // 其他进账户页
}
return $redirect_to;
}, 10, 3 );
三、登出后回首页(而不是登录页)
add_action( 'wp_logout', function() {
wp_safe_redirect( home_url() );
exit;
} );
四、登录页加提示文字
add_filter( 'login_message', function( $message ) {
$notice = '<p class="message">请使用本站账号登录,遇到问题请联系管理员。</p>';
return $notice . $message;
} );
五、登录失败时不提示具体原因(防爆破信息泄露)
默认登录失败会提示”用户名无效”或”密码错误”,等于告诉攻击者用户名是否存在。统一成模糊提示:
add_filter( 'login_errors', function( $error ) {
return '用户名或密码不正确。';
} );
六、彻底自定义登录页(独立页面)
如果要做完全独立的登录页(带品牌背景、注册引导),建议做一个页面模板:
/*
* Template Name: 自定义登录
*/
get_header();
if ( is_user_logged_in() ) {
echo '<p>您已登录,<a href="' . esc_url( wp_logout_url( home_url() ) ) . '">退出</a></p>';
get_footer();
return;
}
$err = '';
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['mmf_login_nonce'] ) ) {
if ( ! wp_verify_nonce( $_POST['mmf_login_nonce'], 'mmf_login' ) ) {
$err = '请求已过期,请刷新重试。';
} else {
$creds = array(
'user_login' => sanitize_user( $_POST['log'] ),
'user_password' => $_POST['pwd'],
'remember' => isset( $_POST['rememberme'] ),
);
$user = wp_signon( $creds, false );
if ( is_wp_error( $user ) ) {
$err = '用户名或密码不正确。';
} else {
wp_safe_redirect( home_url( '/member/' ) );
exit;
}
}
}
?>
<div class="mmf-login-wrap">
<?php if ( $err ) : ?>
<p class="error"><?php echo esc_html( $err ); ?></p>
<?php endif; ?>
<form method="post">
<?php wp_nonce_field( 'mmf_login', 'mmf_login_nonce' ); ?>
<p><input type="text" name="log" placeholder="用户名或邮箱" required></p>
<p><input type="password" name="pwd" placeholder="密码" required></p>
<p><label><input type="checkbox" name="rememberme"> 记住我</label></p>
<p><button type="submit">登录</button></p>
</form>
</div>
<?php get_footer();
七、修改登录地址(要不要做)
装插件 “WPS Hide Login” 或 “Rename wp-login.php”,把 /wp-login.php 改成 /my-secret-entry/。
真实收益:能减少 90% 以上的自动化撞库。安全通过隐藏(security by obscurity)不是真正的防护,但对付无差别扫描确实有效。
风险与注意:
- 改完自己也要记住新地址,建议存密码管理器;
- 插件停用了要能恢复,否则可能进不去后台;
- 有些插件/主题硬编码了
wp-login.php链接,改地址后这些链接会 404; - 不要以为改了地址就安全了,双因素认证和强密码才是根本。
更稳妥的替代方案:不改地址,而是加一层 HTTP 认证(Nginx/Apache 层),或者用 Cloudflare WAF 限制登录页的访问地区/IP。
location ~* ^/wp-login\.php$ {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
注意:加了 HTTP Basic Auth 后,部分 REST API 请求会被挡,导致区块编辑器”更新失败”。要在 auth 规则里放行
/wp-json/和/wp-admin/admin-ajax.php。
常见踩坑与失败案例
- 案例一:改了 Logo 后图片不显示。原因是
get_stylesheet_directory_uri()指向子主题,图片放错了目录。用浏览器开发者工具看实际请求路径就能定位。 - 案例二:用 WPS Hide Login 改了地址,结果自己也忘了,且插件被误停用,彻底进不去后台。恢复方法:通过 FTP 删除
wp-content/plugins/wps-hide-login目录,地址自动还原为默认。改之前一定要记录新地址。 - HTTP Basic Auth 挡掉了 AJAX:后台一堆功能失效(尤其是
admin-ajax.php)。上面给的规则要拆开,只保护wp-login.php,并放行 admin-ajax。 - 自定义登录页没做 nonce:上面代码里的
wp_nonce_field不能省,否则有 CSRF 风险。 - 登录后重定向死循环:
login_redirect里返回的地址如果又要求登录(比如会员页设置成必须登录且跳转回登录页),会无限循环。加判断:
php
if ( strpos( $redirect_to, 'wp-login.php' ) !== false ) {
return home_url();
}
login_enqueue_scripts里直接 echo CSS 没生效:要用wp_enqueue_style或写在login_head里。上面的<style>写法在login_enqueue_scripts钩子里是可行的,但更规范的是:
php
add_action( 'login_enqueue_scripts', function() {
wp_enqueue_style(
'mmf-login',
get_stylesheet_directory_uri() . '/assets/css/login.css',
array(),
'1.0'
);
} );
- 缓存插件缓存了登录页:登录页绝对不能被缓存,否则用户 A 看到用户 B 的登录状态。所有缓存插件默认排除
wp-login.php,但如果用了自定义登录页,要手动排除那个页面 URL。
总结
登录页定制分两块:品牌化用 login_enqueue_scripts + login_headerurl + login_headertext 三个钩子就能搞定,代码量二十行;安全收口优先做”登录失败模糊提示 + 强密码 + 双因素”,改登录地址(WPS Hide Login)和 Nginx 层 HTTP 认证作为额外防线。两个必须记住的坑:改地址前把新地址存好并知道插件停用后的恢复方法;加 HTTP 认证时务必放行 /wp-json/ 和 admin-ajax.php,否则后台大面积功能失效。
