Nginx反向代理

Posted by

一.proxy_set_header的设置:

1.proxy_set_header Host:

proxy_set_header Host $host;

作用是把原http请求的Header中的Host字段也放到转发的请求中,如果没有这一行,nginx转发的请求header里面就不会有Host字段,而服务器是靠这个Host值来区分我们请求的是nage域名资源的

代理的后端服务器可以通过Host头得知用户访问的真正的域名,如果不设置,则得到代理服务器(nginx)的IP,这样对于动态拼接的url,后端服务器能在页面返回正确的url

注意,$host中仅仅包含域名或ip,不会包含端口,如果此时代码中有重定向路由,那么重定向时就会丢失端口信息,导致404

proxy_set_header Host $http_host;

浏览器直接访问访问nginx,获取到的Host包含浏览器请求的IP和端口

proxy_set_header Host $host:$proxy_port;

浏览器直接访问nginx,获取到的Host是$host:$proxy_port,也就是IP:端口

2.proxy_set_header X-Real-IP:

proxy_set_header X-Real-IP $remote_addr;

使web端获得用户真实的IP地址,

注意:$remote_addr只能获取到与服务器本身直连的上层请求ip,所以设置$remote_addr一般都是设置在第一个代理上面,当一个请求通过多个代理服务器时,用户的IP会被代理服务器的IP覆盖

// 在第一个代理服务器中设置
set x_real_ip = $remote_addr

// 最后一个代理服务器中获取
$x_real_ip = IP1

但是问题是,有时候是通过 cdn 访问过来的,那么后面web服务器获取到的永远都是 cdn 的 ip 而非真实用户 ip,这个时候就需要用到X-Forwarded-For——这个变量的意思就像是链路反追踪,从客户的真实ip为起点,穿过多层级的proxy,最终到达web服务器,都会记录下来,所以在获取用户真实ip时,一般就可以设置成:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

这样就能获取所有的代理ip和客户ip。

3.proxy_set_header X-Forwarded-For:

X-Forwarded-For 变量,这是一个squid开发的,用于识别通过HTTP代理或负载平衡器原始IP一个连接到Web服务器的客户机地址的非rfc标准,如果有做X-Forwarded-For设置的话,每次经过proxy转发都会有记录,格式就是 client1,proxy1,proxy2,以逗号隔开各个地址,由于它是非rfc标准,所以默认是没有的,需要强制添加。

在默认情况下经过proxy转发的请求,在后端看来远程地址都是proxy端的ip 。也就是说在默认情况下我们使用request.getAttribute(“X-Forwarded-For”)获取不到用户的ip,如果我们想要通过这个变量获得用户的ip,我们需要自己在nginx添加配置:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

意思是增加一个$proxy_add_x_forwarded_for到X-Forwarded-For里去,注意是增加,而不是覆盖,当然由于默认的X-Forwarded-For值是空的,所以我们总感觉X-Forwarded-For的值就等于$proxy_add_x_forwarded_for的值,实际上当你搭建两台nginx在不同的ip上,并且都使用了这段配置,那你会发现在web服务器端通过request.getAttribute(“X-Forwarded-For”)获得的将会是客户端ip和第一台nginx的ip。

$proxy_add_x_forwarded_for:

 $proxy_add_x_forwarded_for 变量包含客户端请求头中的 X-Forwarded-For$remote_addr 两部分,他们之间用逗号分开。

在第一台 nginx 中使用:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;,现在的$proxy_add_x_forwarded_for变量的X-Forwarded-For部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,于是赋值以后,X-Forwarded-For变量的值就是用户的真实的ip地址了。

到了第二台nginx,使用:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;,现在的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含的是用户的真实ip,$remote_addr部分的值是上一台nginx的ip地址,于是通过这个赋值以后现在的X-Forwarded-For的值就变成了“用户的真实ip,第一台nginx的ip”。

获取客户端的IP地址不仅可以通过proxy_set_header    X-real-ip $remote_addr;获取到,也可以通过proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;

4.proxy_set_header Upgrade:

Upgrade $http_upgrade 和 Connection “upgrade” -如果你的应用程序使用Websockets,则这些header字段是必填字段:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息, 是真正的双向平等对话,属于服务器推送技术的一种。

其他特点包括

  1. 建立在 TCP 协议之上,服务器端的实现比较容易。
  2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  3. 数据格式比较轻量,性能开销小,通信高效。
  4. 可以发送文本,也可以发送二进制数据。
  5. 没有同源限制,客户端可以与任意服务器通信。
  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

协议标识符是ws(如果加密,则为wss),服务器网址就是 URL

ws://lcj.ink:80/path

另外客户端不只是浏览器,只要实现了ws或者wss协议的客户端socket都可以和服务器进行通信。

一个典型的Websocket握手:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

其中的

Upgrade: websocket
Connection: Upgrade

这个就是Websocket的核心了,用来告诉Apache、Nginx等服务器,发起的是Websocket协议,而不是HTTP协议

服务器返回:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。

WebSocket协议相比较于HTTP协议,WebSocket成功握手后还可以多次进行通讯,直到连接被关闭。

从上面我们可以看出,websocket的请求头需要有:

Upgrade: websocket
Connection: Upgrade

因此,我们的nginx反向代理时就需要设置成:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

5.proxy_set_header Accept-Encoding:

proxy_set_header Accept-Encoding gzip;

gzip压缩是否启用,除了服务器支持外,客户端也要支持。
当客户端发送Accept-Encoding:gzip这个request header,服务器即认为其能接受gzip压缩,就响应一个Content-Encoding:gzip,并发送压缩内容;
假如客户端没有发送 Accept-Encoding,那么服务器就把源代码原样输出。

二、proxy_http_version:
proxy_http_version 1 – 定义用于代理的HTTP协议版本,默认情况下将其设置为1.0。对于Websocket和 keepalive 连接,你需要使用1.1版。

proxy_http_version 1.1;

三、proxy_redirect:

重定向:

①不进行重定向(禁止重定向):

proxy_redirect off;

参数off将在这个字段中禁止所有的proxy_redirect指令: proxy_redirect off;

②除了off之外,其余暂时用不到,暂时留空

四、proxy_read_timeout:

proxy_read_timeout 300s;
#连接成功后,后端服务器响应时间(代理接收超时)

五、proxy_connect_timeout:

proxy_connect_timeout 300s;
#nginx和后端服务器连接超时时间(代理连接超时)

六、proxy_pass:

指定反向代理的web服务器,用户发出的请求会被nginx反向代理发送给这个web服务器,这个web服务器发出的信息则又会通过nginx发送给用户

proxy_pass http://127.0.0.1:8080;
#反向代理 http://127.0.0.1:8080

举例:

server {
    listen 80;
    listen [::]:80;
    server_name lcj.ink;
    root /var/www/html;
    index index.html;
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    location ^~ /test {
        proxy_pass http://127.0.0.1:1234;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
        proxy_read_timeout 300s;
    }
}

Nginx反向代理grpc:

gRPC必须使用HTTP/2传输数据,支持明文和TLS加密数据,支持流数据的交互。

因为反向代理的是grpc,

proxy_pass 变成了 grpc_pass

proxy_read_timeout 变成了 grpc_read_timeout

proxy_setd_timeout 变成了 grpc_send_timeout

grpc应用的主要就是这三个

①Nginx以不加密的方式代理不加密的grpc服务:

server {
    listen 80 http2;
    location / {
        grpc_pass grpc://localhost:8200;
    }
}

指令grpc_pass用来指定代理的gRPC服务器地址,前缀协议有两种:

grpc://:与gRPC服务器端交互式以明文的方式

grpcs://:与gRPC服务器端交互是以TLS加密的方式

②Nginx与加密方式代理不加密GRPC服务:

server {
    listen 443 ssl http2;
    ssl_certificate ssl/server.crt;
    ssl_certificate_key ssl/server.key;
    location / {
        grpc_pass grpc://localhost:8200;
    }
}

Nginx根据路径代理到不同的GRPC服务:

server {
    listen 80 http2;
    location /helloworld.Greenter {
        grpc_pass grpc://192.168.1.11:8200;
    }
    location /helloworld.Dispatcher {
        grpc_pass grpc://192.168.1.12:8200;
    }
}

实例,哪吒探针,用nginx反向代理gRPC端口(支持Cloudflare CDN):

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name data.example.com;
    ssl_certificate /data/letsencrypt/fullchain.pem;
    ssl_certificate /data/letsencrypt/privkey.pem;

    underscores_in_headers on;

    location / {
        grpc_read_timeout 300s;
        grpc_send_timeout 300s;
        grpc_pass grpc://localhost:5555;
    }
}

注:

underscores_in_headers默认为off,即默认忽略带下划线的header

underscores_in_headers on;也就是不忽略带下划线的header