发布时间:2026-09-24 12:32 更新时间:2026-09-24 12:32 阅读量:0
把站点切到 HTTPS 之后,前台一切正常,偏偏 /wp-admin 一登录就报「重定向次数过多」,浏览器地址栏在 http 与 https 之间来回跳。这种情况多半不是证书问题,而是 WordPress 判断「当前请求是不是 HTTPS」时判断错了:它以为你在走 HTTP,于是把你往 HTTPS 跳;中间的反向代理或 CDN 又把请求头还原成 HTTP 再送回来,两边来回踢皮球。搞清 is_ssl() 的取值来源和反向代理头有没有透传,问题基本就能定位。
WordPress 核心函数 is_ssl() 位于 wp-includes/load.php,它按顺序检查三样东西:$_SERVER['HTTPS'] 是否等于 on、1 或 true;$_SERVER['SERVER_PORT'] 是否等于 443;以及 $_SERVER['HTTP_X_FORWARDED_PROTO'] 是否等于 https。三者满足其一,就认定当前是加密连接。
问题就出在这里:请求先到 CDN 或前置 Nginx,用户到前置这一跳是 HTTPS,但前置回源到你的服务器时,如果用的是 HTTP 回源,那么落到 PHP 里的 $_SERVER 全是 HTTP 的痕迹——HTTPS 不存在、端口是 80、X-Forwarded-Proto 又没传过来。WordPress 于是判定「这是 HTTP 请求」,后台里的 force_ssl_admin 逻辑就发起跳转,跳到 https 版本;跳过去之后回源依旧是 HTTP 形态,再次判定失败,循环形成。
排查时不要急着改数据库,先确认请求到达 PHP 时长什么样。临时在站点根目录放一个探测文件,访问后立刻删除:
cat > /www/wwwroot/example.com/_s.php <<'EOF'
<?php
var_dump($_SERVER['HTTPS'] ?? null);
var_dump($_SERVER['SERVER_PORT'] ?? null);
var_dump($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? null);
var_dump($_SERVER['HTTP_X_FORWARDED_SSL'] ?? null);
EOF
curl -sI https://example.com/_s.php | head -n 5
如果 HTTPS 为 null、端口是 80、X_FORWARDED_PROTO 也没有值,那就说明代理信息确实丢在链路上了。注意这类探测文件暴露服务器信息,验证完务必 rm 掉。
正规做法是让前置把真实协议告诉后端。Nginx 反代场景下,在代理配置里补上这几个头,注意 $http_x_forwarded_proto 与 $scheme 的选用要结合你的链路层级:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
如果前面还有一层 CDN,CDN 到源站这一跳是 HTTP,那么 $scheme 在源站只会是 http,需要用 CDN 传下来的 $http_x_forwarded_proto 覆盖,否则源站永远认为自己在跑 HTTP。改完执行 nginx -t && nginx -s reload,再访问一次探测文件确认头已经出现。
另一条路是在 wp-config.php 里做兜底判断,适合无法改动代理层、或者多层代理头不可靠的环境。把下面这段放在 /* That's all, stop editing! */ 这行之前:
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
if ( ! empty( $_SERVER['HTTP_CF_VISITOR'] )
&& false !== strpos( $_SERVER['HTTP_CF_VISITOR'], 'https' ) ) {
$_SERVER['HTTPS'] = 'on';
}
define( 'FORCE_SSL_ADMIN', true );
define( 'FORCE_SSL_LOGIN', true );
这段代码的作用是:在 WordPress 加载判断逻辑之前,手动把 $_SERVER['HTTPS'] 置为 on,让 is_ssl() 得到正确结果。第二段针对 Cloudflare 的 CF_VISITOR 头,是否使用取决于你的 CDN 厂商,具体头名称以对应厂商官方文档为准。这样即使代理头不全,后台也不会再被误判成 HTTP。
首先是 wp_options 表里的 siteurl 和 home,两个值必须都写成 https:// 开头。有一个还是 http,WordPress 生成的后台链接就会带着 http 出去,跳转依旧存在。可以用 WP-CLI 快速核对:
wp option get siteurl --path=/www/wwwroot/example.com
wp option get home --path=/www/wwwroot/example.com
wp option update siteurl 'https://example.com' --path=/www/wwwroot/example.com
wp option update home 'https://example.com' --path=/www/wwwroot/example.com
其次是 SSL 插件与服务器层规则的叠加。很多人同时开了 Really Simple SSL 插件、又在 Nginx 里写了强制跳转、CDN 面板里还开了「Always Use HTTPS」,三处各跳一次,只要中间任何一环判断失败就会互相打架。建议只保留一层强制跳转,其余关闭。Nginx 侧的常规写法如下,注意不要对 /wp-admin 再做额外特殊跳转:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
另外要检查缓存插件和 CDN 缓存里是否残留了旧的重定向响应。301 是会被浏览器和中间缓存长期记住的,测试阶段建议用无痕窗口,或者把跳转临时改成 302 观察。宝塔面板用户还要确认站点配置文件没有被其他规则文件覆盖,改动后以面板里的重载结果和 nginx -t 输出为准。
总结一下排查顺序:先用探测文件确认 PHP 实际收到的协议头,缺失就补 X-Forwarded-Proto,补不进去就在 wp-config.php 里强制 $_SERVER['HTTPS'];然后核对 siteurl、home 两个选项;最后清理掉多余的强制跳转层,只留一处。整套动作做完,后台的重定向循环基本就消失了。如果仍有异常,建议把 Nginx 访问日志里的 301 记录单独筛出来看跳转链路,哪一跳在反复出现,问题就在哪一层。
| 📑 | 📅 |
|---|---|
| 宝塔面板Nginx伪静态改了不生效:规则文件位置与重载验证 | 2026-09-24 |
| WordPress数据库连不上但宝塔显示正常:主机名与socket排查 | 2026-09-24 |
| WordPress改文章前台不变:三层缓存清理顺序与验证 | 2026-09-24 |
| 宝塔面板磁盘挂载与网站目录迁移:/www 换盘后的路径、权限与面板数据同步 | 2026-09-24 |
| 网站被CDN缓存住旧页面:缓存键、边缘刷新与源站头冲突排查 | 2026-09-24 |
| 域名下手机跳m站收录分散:自适应与跳转取舍 | 2026-09-24 |
| 宝塔面板定时任务备份到对象存储:命令行工具安装、密钥权限与保留份数设置 | 2026-09-24 |
| PHP-FPM 进程数怎么调:pm.max_children 与内存换算、502 反复排查 | 2026-09-25 |
| MySQL只允许本机连接后网站报错:bind-address与用户host授权取舍 | 2026-09-25 |
| WordPress文章ID与固定链接优化:伪静态改动后旧链接兼容 | 2026-09-25 |