nginx反向代理配置upstream要注意的问题
通过IP做反向代理
假设有两台服务器:
A:https://test2.xiebruce.top,IP:1.1.1.1
B:https://2.2.2.2:440,IP:2.2.2.2
两台服务器均配置了https支持(即ssl证书),现在让A为反向代理服务器,B为被代理服务器,即我访问https://test2.xiebruce.top,实际上是访问https://2.2.2.2:440中的服务。
B服务器配置
B服务器是实际提供服务的服务器,网站代码在该服务器上,由于我只是测试,所以我直接开启了autoindex(目录浏览)功能:
server {
listen 440;
server_name 2.2.2.2;
#日志
access_log /data/wwwlogs/test.xiebruce.access.log combined;
error_log /data/wwwlogs/test.xiebruce.error.log error;
root /data/wwwroot/test.xiebruce.top;
autoindex on; #开启nginx目录浏览功能,on为开启,off为关闭
autoindex_exact_size on; #显示文件大小从KB显示
autoindex_localtime on; #显示文件修改时间,为服务器本地时间
# https相关
ssl on;
ssl_certificate /etc/letsencrypt/live/xiebruce.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xiebruce.top/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHellA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location ~ [^/]\.php(/|$) {
#fastcgi_pass remote_php_ip:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
}
说明:通过以上配置,要求能通过浏览器正常访问https://2.2.2.2:440/index.php,不过实际环境该地址一般都是内网地址,不能被外部访问,但可以被内部的另一台服务器访问,在这里,另一台服务器就是指代理服务器。
A服务器配置
A服务器是代理服务器,本身没有实际的网站程序代码,所有请求都会转发给B服务器:
upstream load_balance {
server 2.2.2.2:440 weight=1;
}
server {
listen 443;
server_name test2.xiebruce.top;
access_log /data/wwwlogs/test2.xiebruce.access.log combined;
error_log /data/wwwlogs/test2.xiebruce.error.log error;
# https相关
ssl on;
ssl_certificate /etc/letsencrypt/live/xiebruce.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xiebruce.top/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHellA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host 2.2.2.2:440;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://load_balance;
}
}
说明:test2.xiebruce.top已通过dns解析到1.1.1.1(比如在阿里云添加解析),用户访问https://test2.xiebruce.top,请求将会到达该server模块。
这句是设置Http header的Host字段,该字段其实就是要请求的vhost的server_name,服务器B在收到A服务器的请求后,会根据这个Host值去找端口为440(即listen 440),server_name为2.2.2.2的vhost(一个nginx的server模块叫一个vhost),所以这个Host的值不要直接写$http_host
,如果写$http_host
的话,那它的值就为当前服务器的server_name即test2.xiebruce.top,B服务器上肯定找不到server_name为test2.xiebruce.top的vhost,因为B服务器的vhost server_name是2.2.2.2啊。
proxy_set_header Host 2.2.2.2:440;
这句是设置协议,该设置决定是使用http还是https协议去访问B服务器,由于B服务器配置了https协议,所以X-Forwarded-Proto
必须为https,否则无法通过,会返回400 Bad Request错误,而由于$scheme
为A服务器的协议,而刚好A服务器的协议也是https,所以$scheme
的值实际上就是https。
proxy_set_header X-Forwarded-Proto $scheme;
所以我们也可以自己写死https
:
proxy_set_header X-Forwarded-Proto 'https';
因为万一A服务器用的不是https而是http,那么$scheme
变量的值就是http
,而用http协议去请求https协议的vhost,肯定被拒绝,所以最好直接写死。
$remote_addr
是远程客户端ip,在这里就是指访问test2.xiebruce.top域名的电脑所在的网络的外网ip,在B服务器上的代码比如php可以用$_SERVER['HTTP_X_REAL_IP']
获取到这个ip。
proxy_set_header X-Real-IP $remote_addr;
需要注意的是,这只是一级代理,如果是两级以上代理,比如实际网站代码在C机器上,我们访问的是A,A反代到B,B再反代到C,那么A的nginx配置需要用这句设置X-Real-IP的值,但B就不用配置这句,直接把这句去掉,如果B也像A这样设置,那么这个ip将会变成A的ip而不是客户端的ip,因为对B来说,A就是B的客户端,如果B中不设置,那这个值就会是实际客户端(即浏览器所在的外网ip)的值(实测)。
X-Forwarded-For是用于记录代理信息的,每经过一级代理,它就会记录一个ip,多级是用逗号分隔的,比如上面说的,A、B、C三台机,假设他们的ip分别为:1.1.1.1、2.2.2.2、3.3.3.3,客户端(浏览器)的ip为23.45.67.78,浏览器访问A,A代理到B,B代理到C,那么在C机器中获取到的X-Forwarded-For的值,将是23.45.67.78,1.1.1.1
,我自己的记忆办法是:只要你请求了代理服务器,那么你的ip就会出现在X-Forwarded-For中,比如客户端(浏览器)请求了A,A是代理服务器,所以客户端的ip出现在X-Forwarded-For中,同样,A请求了B,因为B是代理服务器,所以A的ip出现在X-Forwarded-For中,B请求了C,但由于C不是代理服务器,所以B的ip没有出现在X-Forwarded-For中,也就是不取决于你本身是否为代理服务器,而是取决于你是否请求了代理服务器。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
当只有一级代理时(比如本例就是),X-Real-IP与X-Forwarded-For的值相同。
这句代表把请求反向代理到名为load_balance
的upstream模块中,load_balance
是一个自定义的名字,只要两边对上了,写啥名字都行,但要注意前面的https://
,这个是拼到upstream指定的server中的,比如现在upstream中的server为:2.2.2.2:440,那么实际请求的时候,将会把https://load_balance中的load_balance替换为2.2.2.2:440,即最终请求的是:https://2.2.2.2:440,所以https://2.2.2.2:440必须可以被A服务器访问(但未必能被外网访问,因为这ip有可能是内网ip,而且一般情况下都是内网ip)。
proxy_pass https://load_balance;
通过域名做反向代理
A:https://test2.xiebruce.top,IP:1.1.1.1
B:https://test.xiebruce.top:440,IP:2.2.2.2
两台服务器均配置了https支持(即ssl证书),现在让A为反向代理服务器,B为被代理服务器,即我访问https://test2.xiebruce.top,实际上是访问的https://test.xiebruce.top中的服务。
B服务器
server {
listen 440;
server_name test.xiebruce.top;
access_log /data/wwwlogs/test.xiebruce.access.log combined;
error_log /data/wwwlogs/test.xiebruce.error.log error;
root /data/wwwroot/test.xiebruce.top;
autoindex on; #开启nginx目录浏览功能,on为开启,off为关闭
autoindex_exact_size on; #显示文件大小从KB显示
autoindex_localtime on; #显示文件修改时间,为服务器本地时间
ssl on;
ssl_certificate /etc/letsencrypt/live/xiebruce.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xiebruce.top/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHellA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location ~ [^/]\.php(/|$) {
#fastcgi_pass remote_php_ip:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
}
A服务器
upstream load_balance {
server test.xiebruce.top:440 weight=1;
}
server {
listen 443;
server_name test2.xiebruce.top;
access_log /data/wwwlogs/test.xiebruce.access.log combined;
error_log /data/wwwlogs/test.xiebruce.error.log error;
ssl on;
ssl_certificate /etc/letsencrypt/live/xiebruce.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xiebruce.top/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHellA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host test.xiebruce.top;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://load_balance;
}
}
其实跟ip方式是一样的:
– upstream中的server指向的域名和端口,要与被代理机器上的server_name及listen的端口相同。
– proxy_set_header中的Host要与被代理机器上的server_name相同
– proxy_set_header中的X-Forwarded-Proto要与被代理机器的协议相同(即http或https)
– proxy_pass要以http://开头还是https://开头,要看被代理机是http访问的还是https访问的,跟它对上就行。
不使用upstream代理
test2.xiebruce.top为代理服务器,https://test.xiebruce.top:440为被代理服务器,访问https://test2.xiebruce.top,实际上是访问https://test.xiebruce.top:440提供的服务:
server {
listen 443;
server_name test2.xiebruce.top;
access_log /data/wwwlogs/test.xiebruce.access.log combined;
error_log /data/wwwlogs/test.xiebruce.error.log error;
ssl on;
ssl_certificate /etc/letsencrypt/live/xiebruce.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xiebruce.top/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHellA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host test.xiebruce.top;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://test.xiebruce.top:440;
}
}
很简单,直接把上边upstream中的server写到proxy_pass后面就行了,由这也可以看出来,这样做反向代理,只能反代到一台服务器,upstream的功能正是为了解决反代到多台服务器,并且在这多台服务器之间做负载均衡。
以上内容均经过实际测试,只是ip被我替换成了两个示例ip,如有错误,恳请指正!