本文完整阅读约需 10 分钟,如时间较长请考虑收藏后慢慢阅读~

如今从HTTP到HTTPS的迁移已经成为了Web站点的趋势,毕竟所有人都不愿意看见自己的网站被最新的浏览器标记为不安全,有一把小锁在地址栏上也能给用户以安全感。可这一迁移过程由于Web服务器不同实现所导致的大量历史遗留问题以及业务代码水平的参差不齐,容易遇到各种问题,尤其在WordPress这样一个复杂的CMS系统上。这篇博客来为大家讲解WordPress采用反向代理升级到HTTPS所遇到的典型问题之一。

如果我们原先的WordPress使用Nginx或Apache作为Web服务器,而直接修改Web服务器到HTTPS有一定困难(例如必须使用Docker,或者原先的服务器由于一些原因,不支持HTTPS等),那么就需要通过反向代理实现从HTTP到HTTPS的迁移。

可是WordPress内部使用了大量的$_SERVER['PHP_SELF']等语句对当前网络协议进行判断,导致了尽管反向代理使用了HTTPS,WordPress依旧会以为当前是在HTTP情况下进行访问(事实也确实是如此,只是这里新的Nginx服务器作为客户端对旧的Web服务器发起HTTP请求)

这一问题看似不会造成影响(毕竟只要对HTTP设置302跳转,就可以自动跳转到HTTPS),可当配置完成(包括数据库中所有与http相关的url进行replace到https)之后会发现,所有静态文件请求都被浏览器拦截,并且无法访问WordPress后台(提示重定向次数过多)

既然是重定向次数过多,那么我们就有必要来看看源码中到底是什么导致了重定向:

首先我们搜索源码中包含对网络协议判断($_SERVER)的部分,可以看到,有大量的代码使用了该方式进行网络协议判断,其中./wp-includes/functions.php是WordPress几乎所有公共函数的集合,我们首先来看这个文件:

部分源码如下:

/**
 * Whether to force SSL used for the Administration Screens.
 *
 * @since 2.6.0
 *
 * @staticvar bool $forced
 *
 * @param string|bool $force Optional. Whether to force SSL in admin screens. Default null.
 * @return bool True if forced, false if not forced.
 */
function force_ssl_admin( $force = null ) {
    static $forced = false;

    if ( !is_null( $force ) ) {
        $old_forced = $forced;
        $forced = $force;
        return $old_forced;
    }

    return $forced;
}

结合源码与文档,我们明白了这个函数的含义就是强制跳转相关的逻辑,而当我们配置WordPress站点为https,它却检测到Nginx是以HTTP方式访问的时候,就会尝试跳转到https,然后周而复始……

我们同时来看看./wp-login.php中相关的源码:
部分源码如下:

// Redirect to https login if forced to use SSL
if ( force_ssl_admin() && ! is_ssl() ) {
    if ( 0 === strpos($_SERVER['REQUEST_URI'], 'http') ) {
        wp_safe_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
        exit();
    } else {
        wp_safe_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
        exit();
    }
}

该源码起到的作用和之前functions.php的作用基本一致,此处不再赘述。

分析到这里,我们可以显然发现是WordPress对于协议的判断存在过于死板的情况,如果配置WordPress站点为http,那么在以https方式访问站点的时候,静态文件会以http方式进行请求,被浏览器拦截,而配置WordPress站点为https的时候,又会因为这个业务逻辑问题,导致无限的重定向。

解决方法呢,其实非常简单,既然是WordPress对于网络协议判断失误,而网络协议主要是由Web服务器提供,那我们就来修改Web服务器(旧,非反代),来让其提供HTTP服务的时候,告诉WordPress其实请求的是HTTPS。

如果你用Nginx

在配置文件指定位置加入以下一行(如图):

fastcgi_param  HTTPS on;

然后重启Nginx,修改WordPress数据库中所有http相关字段为https,即可正常运行。

如果你用Apache

打开你的Apache配置文件,在相关的VirtualHost标签内加入以下一行(没图):

SetEnvIf X-Scheme "^https$" HTTPS=on

重启Apache,修改WordPress数据库中所有http相关字段为https,即可正常运行。