解决https负载均衡遇到的问题

使用php判断当前域名的时候。会遇到,命名我们使用的是https访问。但是为什么我们获取到的是http

Posted by 张晨 on June 30, 2021

最近在配置阿里云函数计算的时候遇到一个问题。就是我明明配置了https访问我的web网站。但是我在里面获取当前域名的时候,却给我返回的是http。其实这个原理和负载均衡的原理是一样的。就是请求被反向代理了。所以今天的解决方案也是用负载均衡来进行演示和讲解。

环境准备

  1. 准备一个可以访问的yii2网站。nginx配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server {
    listen       80;
    server_name  _ default_server;
  
    root         "/mnt/www/web";
  
    index        index.php index.html index.htm;


    # PHP-FPM Definition
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }
    location ~ \.php?$ {
        try_files $uri = 404;
        include fastcgi_params;
        fastcgi_keep_conn off;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_split_path_info ^(.+\.php)(.*)$;

        fastcgi_pass 127.0.0.1:9000;
        fastcgi_read_timeout 180;

        fastcgi_index index.php;
        fastcgi_intercept_errors on;
    }
}
  1. 修改yii项目的代码。让其直接显示当前域名。

阿里云函数计算环境说明

在阿里云函数计算的地方,我们是可以自定义域名的。但是我们的域名的https是直接在阿里云函数计算上面配置的。我们的服务始终只能监听一个80端口。然后函数计算会将请求转发到我们的80端口上面来。

模拟阿里云函数计算https

在nginx下面配置一个域名访问的https。然后将请求代理到80端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
    listen       443 ssl;
    server_name  www.zrongdong.com;

    # ssl证书地址
    ssl_certificate     /mnt/httpd/conf/ssl/ssl.pem;  # pem文件的路径
    ssl_certificate_key  /mnt/httpd/conf/ssl/ssl.key; # key文件的路径

    # ssl验证相关配置
    ssl_session_timeout  5m;    #缓存有效期
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;    #加密算法
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;    #安全链接可选的加密协议
    ssl_prefer_server_ciphers on;   #使用服务器端的首选算法

    location / {
        proxy_pass http://127.0.0.1:80;
        index  index.html index.htm;
    }
}

使用https访问网站

问题来了。我们明明访问的是https的域名。为什么显示我们的域名是http://127.0.0.1啊?

讲解

其实我们是因为我们使用了反向代理。

客户端访问的是https服务。然后https在帮我们去访问真正的http这个网站。而且刚好,http这个网站就是在本地的127.0.0.1:80所以。对于http网站来说。它就只知道,本地有个人访问它。

我们要怎么处理啦?

处理办法就是,我们在https代理处,需要修改一下请求的头部。告诉http我当前访问的域名和我的ip地址。全部改成客户端访问我们的时候的域名和ip地址。修改如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server {
    listen       443 ssl;
    server_name  www.zrongdong.com;

    # ssl证书地址
    ssl_certificate     /mnt/httpd/conf/ssl/ssl.pem;  # pem文件的路径
    ssl_certificate_key  /mnt/httpd/conf/ssl/ssl.key; # key文件的路径

    # ssl验证相关配置
    ssl_session_timeout  5m;    #缓存有效期
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;    #加密算法
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;    #安全链接可选的加密协议
    ssl_prefer_server_ciphers on;   #使用服务器端的首选算法

    location / {
  		# 代理请求头修改
        proxy_set_header Host $host;    # 请求host
        proxy_set_header X-Real-IP $remote_addr;    # 客户端真实ip地址
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;   # 转发地址
        proxy_set_header X-Forwarded-Proto $scheme; # 转发http模式 此处为https
        
        proxy_set_header Connection "keep-alive";
        proxy_next_upstream error invalid_header http_500 http_502 http_504;
        proxy_next_upstream_timeout 300;
        proxy_next_upstream_tries 2;
        proxy_read_timeout 80s;
        proxy_pass http://127.0.0.1:80;
        index  index.html index.htm;
    }
}

看。我们的地址就变过来了吧。

但是问题还没解决完。命名我们是https访问的。为什么还是获取到的是http啦?

想找到这个问题的答案啦。你需要去看yii2的getHostInfo是怎么获取当前域名的。

代码的重点在这里。一开始使用$_SERVER['HTTPS']判断是否为https。其实这里会判断不到。因为我们没有这个头。

然后使用下面的代码判断是否为https。

诶。我们的nginx的头信息里面,命名加了X-Forwarded-Proto头的啊?为什么没效果嘞?

这个问题困扰我好久,百思不得其解。

问题原来出在这里。在yii2的代码里面。原来是把X-Forwarded-Proto请求头过滤掉了。搞个捶捶。

我们需要配置允许请求头才可以正常。

1
2
3
4
5
6
7
8
9
10
'request' => [
    // ...
    'trustedHosts' => [
        'any' => [
            'X-ProxyUser-Ip',
          	'X-Forwarded-Proto',
            'Front-End-Https',
        ],
    ],
],

大功告成。

总结

其实原理就是很简单。我们在代理的时候,需要记得修改一下请求头。

提示

其实在阿里云函数计算里面。需要在nginx里面修改代理地址为如下才可以表示是https访问。

1
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; # 转发http模式 此处为https

参考文档

没有产考到有效文章。