最近在配置阿里云函数计算的时候遇到一个问题。就是我明明配置了https访问我的web网站。但是我在里面获取当前域名的时候,却给我返回的是http。其实这个原理和负载均衡的原理是一样的。就是请求被反向代理了。所以今天的解决方案也是用负载均衡来进行演示和讲解。
环境准备
- 准备一个可以访问的
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;
}
}
- 修改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
参考文档
没有产考到有效文章。