Nginx反向代理https到http

应用场景:

  • 一台nginx服务器;一台或多台web服务器;
  • Nginx直接对外提供服务,web服务器只对nginx提供服务;

为什么要这样设计呢?直接web服务器对外提供服务不就可以了吗?何必多此一举。一般至少有2个方面的考量:

  1. 多台web服务器都需要对外提供服务,每台服务器上申请一个公网ip和公网带宽,成本太高;
  2. 单台服务器无法应对大并发的压力,需要多台服务器共台分摊;

1*nginx + N*web的架构就完美的解决了这两个问题:首先,nginx负责区分各个web服务器的流量分发到对应的服务器上处理,这便是反向代理;其次,nginx可以将流量根据一次策略分配到相同服务的多台服务器上,这便是负载。这是Nginx最有价值的两大功能点。

部署实例:

Nginx服务器上对外提供https服务,反向代理到3台http服务器上,确保nginx与web服务器至少内网能通,nginx上的nginx.conf配置如下:

#user  nobody;
worker_processes  1;

events {
    worker_connections  1024;
}

http {

    proxy_intercept_errors on;

    upstream webs{
        server 172.17.1.2;
        server 172.17.1.3;
        server 172.17.1.4;
    }

    server {
        listen 443  ssl;
        ssl_certificate cer/server.crt;
        ssl_certificate_key cer/server.key;
        location / {
            proxy_pass  http://webs;
            proxy_set_header    host    $host;
            add_header  Content-Security-Policy "upgrade-insecure-requests";
        }
    }
}

几个配置项需要重点说明:

proxy_pass:反向代理目标,这里可以直接写http://172.17.1.2 这样就是反向代理对这单台服务器,如果有多台则需要先将多台服务器写到一个upstream中,这样就能实现负载,多台服务器轮流对外提供服务;

proxy_set_header:设置代理请求的头部信息,所谓的代理就是当前这台服务器会再次向目标服务器发起请求,拿到结果后再返回给原始请求。

由于重新发起了请求,Http请求的头部信息会和原始请求不一样了,A–>B–>C,在A看来是请求B,在C看来是B请求的,这样一来传输过程就会有偏差。因此我们需要在代理服务器上将信息还原,让C看来是A请求的,而不是B。

在Http请求头部信息中最重要的字段之一是host,这个host决定你请求的是谁?在C看来,所以的请求都第是从nginx发起,目标是 http://webs ,如果服务器中有使用到该字段,则都会变成 http://webs ,比如请求css或js或图片,结果返回给A的时候,A是找不到 http://webs 这台服务器的,这个地址只有nginx能够识别。

因此nginx需要将host改成他接收到的请求中的host,即使用全局变量$host,这样一来nginx将原请求中的host原封不动的“转告”给目标,达到“透传”的目的。这样能解决反向代理中很多资源路径有误的问题;

add_header Content-Security-Policy “upgrade-insecure-requests”

还有一个问题在Https反向代理到http服务器上时经常会碰到,就是https页面中不允许有http请求,错误信息类似:

Mixed Content: The page at 'https://www.yusian.com/bbs/' was loaded over HTTPS, but requested an insecure script 'http://www.yusian.com/bbs/data/cache/common.js?Fmu'. This request has been blocked; the content must be served over HTTPS.

网有上很多贴子说将http请求重定向到https即可,但实际上似乎并不奏效。为什么呢?对于用户主动发起的http请求来讲,确实会被重定向到https,这样一来就没http什么事了,但如果存在反向代理就没这么简单了。

A–>B是https协议,B–>C是http协议,那么问题来了,对于真正提供服务的C,在他看来他对外提供的就是http服务,如果是由他衍生出来的请求就一定是http,这样一来就会出现页面上请求静态资源使用的是http协议,然而原始链接又是https协议,浏览器基于安全性策略,将这些http请求拦截了下来并抛出以上异常。这种行为类似跨域的安全机制一样,是浏览器的行为,并不是服务器不提供这个http服务,而是浏览器不让你去请求这个http资源。

Content-Security-Policy:内容安全策略,这个头的作用有很多,其中一个就是将http请求改成https请求,其他作用就不在这里展开了,在代理的请求中添加这个头部信息即可解决http请求的问题,简单方便,安全无痛苦。

发表评论