nginx反向代理配置upstream要注意的问题

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,如有错误,恳请指正!

打赏

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

nginx反向代理配置upstream要注意的问题