WordPress插件Password Protect重定向循环问题,求修复方案
问题定位与修复方案
重定向循环的核心原因
外部URL被
wp_safe_redirect拦截
WordPress的wp_safe_redirect默认只允许跳转到当前站点域名或已授权的外部域名。如果目标URL是外部站点(比如示例中的https://example.com/a)且未添加到白名单,函数会自动跳转到站点首页,导致插件反复尝试跳转外部URL、又被拦截回首页,形成循环。URL字符串比较逻辑不严谨
直接用$redirect_url != $current_url判断,会因以下场景触发误判:
- URL末尾斜杠差异(如
https://example.com/avshttps://example.com/a/) - HTTP/HTTPS协议不一致
- 存在额外查询参数(如
https://example.com/a?utm_source=xxx) - 带www与不带www的域名差异
is_page函数的局限性is_page('password-protect')仅对WordPress管理的页面生效,如果目标URL是本站静态页、自定义路由或外部页面,该判断会返回false,导致插件错误触发跳转逻辑。
修复步骤与代码调整
1. 授权外部跳转域名(针对外部URL场景)
添加过滤器自动从密码映射中提取目标域名,加入安全跳转白名单:
add_filter('allowed_redirect_hosts', function($hosts) { global $passwords; foreach ($passwords as $url) { $parsed = parse_url($url); if (isset($parsed['host'])) { $hosts[] = $parsed['host']; } } return $hosts; });
2. 优化URL比较逻辑
新增标准化URL函数,统一处理协议、斜杠、域名格式,避免无意义的差异:
function normalize_url($url) { $url = rtrim($url, '/'); $parsed = parse_url($url); if (!$parsed) return ''; $scheme = isset($parsed['scheme']) ? strtolower($parsed['scheme']) : (is_ssl() ? 'https' : 'http'); $host = isset($parsed['host']) ? strtolower($parsed['host']) : $_SERVER['HTTP_HOST']; $path = isset($parsed['path']) ? rtrim($parsed['path'], '/') : ''; return "$scheme://$host$path"; }
3. 替换is_page判断为路径检查
避免依赖WordPress页面检测函数,直接通过URL路径判断是否在密码页:
$password_page_url = normalize_url(site_url('password-protect')); $is_password_page = ($current_url === $password_page_url);
4. 完整修复后的代码
<?php $passwords = array( 'password-a' => 'https://example.com/a', 'password-b' => 'https://example.com/b', ); // 自动授权密码映射中的外部域名 add_filter('allowed_redirect_hosts', function($hosts) { global $passwords; foreach ($passwords as $url) { $parsed = parse_url($url); if (isset($parsed['host'])) { $hosts[] = $parsed['host']; } } return $hosts; }); // 标准化URL,消除协议、斜杠、域名格式差异 function normalize_url($url) { $url = rtrim($url, '/'); $parsed = parse_url($url); if (!$parsed) return ''; $scheme = isset($parsed['scheme']) ? strtolower($parsed['scheme']) : (is_ssl() ? 'https' : 'http'); $host = isset($parsed['host']) ? strtolower($parsed['host']) : $_SERVER['HTTP_HOST']; $path = isset($parsed['path']) ? rtrim($parsed['path'], '/') : ''; return "$scheme://$host$path"; } function password_protect_init() { global $passwords; if (is_admin() || is_user_logged_in()) { return; } $current_url = normalize_url((is_ssl() ? "https://" : "http://") . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); $password_page_url = normalize_url(site_url('password-protect')); $is_password_page = ($current_url === $password_page_url); if (isset($_POST['my_website_password'])) { $password = sanitize_text_field($_POST['my_website_password']); if (array_key_exists($password, $passwords)) { // 增强Cookie安全性,添加SameSite与Secure属性 setcookie('my_website_password', $password, time() + 86400, '/', '', is_ssl(), true); $redirect_url = normalize_url($passwords[$password]); wp_safe_redirect($redirect_url); exit; } else { // 密码错误时跳回密码页 wp_safe_redirect($password_page_url); exit; } } if (isset($_COOKIE['my_website_password']) && array_key_exists($_COOKIE['my_website_password'], $passwords)) { $redirect_url = normalize_url($passwords[$_COOKIE['my_website_password']]); // 仅当当前URL不等于目标URL且不在密码页时跳转 if ($current_url !== $redirect_url && !$is_password_page) { wp_safe_redirect($redirect_url); exit; } } elseif (!$is_password_page) { wp_safe_redirect($password_page_url); exit; } } add_action('wp', 'password_protect_init', 10); function password_protect_template_redirect() { $password_page_path = parse_url(site_url('password-protect'), PHP_URL_PATH); $current_path = parse_url((is_ssl() ? "https://" : "http://") . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH); if ($current_path === $password_page_path) { ?> <html> <head> <title>Password Protected</title> </head> <body> <form method="post"> <label for="my_website_password">Password:</label> <input type="password" id="my_website_password" name="my_website_password"> <input type="submit" value="Submit"> </form> </body> </html> <?php exit; } } add_action('template_redirect', 'password_protect_template_redirect'); function password_protect_login_redirect($redirect_to) { if (!isset($_GET['redirect_to']) || $_GET['redirect_to'] == 'wp-admin/') { $redirect_to = site_url('password-protect'); } return $redirect_to; } add_filter('login_redirect', 'password_protect_login_redirect');
额外优化建议
- 添加密码错误提示:在密码页显示错误信息,提升用户体验
- 限制密码尝试次数:防止暴力破解
- 支持多语言:将密码页文本改为可翻译字符串
内容的提问来源于stack exchange,提问作者Noel Bürgler
相关产品推荐
相关产品推荐

