代理 HTTP 流量

配置方法

代理所有流量,将所有请求原封不动转发出去。

  1. http {
  2. server {
  3. listen 8080;
  4. resolver 114.114.114.114;
  5. location / {
  6. proxy_pass http://$http_host$request_uri;
  7. }
  8. }
  9. }

也可以根据 request uri 的不同,将请求代理到不同的后端服务器。

  1. http {
  2. server {
  3. listen 8080;
  4. resolver 114.114.114.114;
  5. location /foo/ {
  6. proxy_set_header Host foo.com;
  7. proxy_pass http://192.168.0.10:80;
  8. }
  9. location /bar/ {
  10. proxy_set_header Host bar.com;
  11. proxy_pass http://192.168.0.20:80;
  12. }
  13. }
  14. }

使用方法

方式1:通过 IP 地址直接访问代理,以 request uri 作为路由规则。

  1. suhua@g7-7588:~$ curl http://192.168.0.100:8080/foo/index.html -v
  2. * Trying 192.168.0.100...
  3. * TCP_NODELAY set
  4. * Connected to 192.168.0.100 (192.168.0.100) port 8080 (#0)
  5. > GET /foo/index.html HTTP/1.1
  6. > Host: 192.168.0.100:8080
  7. > User-Agent: curl/7.58.0
  8. > Accept: */*

方式2:设置浏览器代理
image.png

代理 HTTPS 流量

官方模块

配置方法

使用官方的 ngx_stream_ssl_preread_module 模块,它是基于 TCP 的代理。

  1. stream {
  2. server {
  3. listen 443;
  4. resolver 114.114.114.114;
  5. ssl_preread on;
  6. proxy_connect_timeout 5s;
  7. proxy_pass $ssl_preread_server_name:$server_port;
  8. }
  9. }

根据域名进行转发。注意,此时并不需要设置 proxy_set_header 进行 Host,可以理解为 TCP 代理 HTTPS 流量,是原封不动地转发 HTTP 流量,而 HTTP 中的 Host 并不会发生修改。

  1. stream {
  2. map $ssl_preread_server_name $upstream {
  3. www.foo.com foo;
  4. foo.com foo;
  5. bar.cn bar;
  6. www.bar.cn bar;
  7. default default_upstream;
  8. }
  9. upstream foo {
  10. server 192.168.0.10:443;
  11. }
  12. upstream bar {
  13. server 192.168.0.20:443;
  14. }
  15. upstream default_upstream {
  16. server 127.0.0.1:443;
  17. }
  18. server {
  19. listen 443;
  20. resolver 114.114.114.114;
  21. ssl_preread on;
  22. proxy_connect_timeout 5s;
  23. proxy_pass $upstream;
  24. }
  25. }

使用方法

通过修改 hosts 文件,将域名直接指向代理服务器。

  1. 192.168.0.100 foo.com
  2. 192.168.0.100 bar.com

因为该方式流量会直接通过 TCP 穿透,所以不需要配置浏览器代理。而后端服务器一般不支持 HTTP CONNECT 连接,所以配置了浏览器代理后,反而很可能会无法工作。

第三方模块

配置方法

使用淘宝开发的 ngx_http_proxy_connect_module 模块

  1. http {
  2. server {
  3. listen 8443;
  4. resolver 114.114.114.114 ipv6=off;
  5. resolver_timeout 5s;
  6. #if ($host !~ '^(.*?\.)?(baidu\.com|google\.com)$') {
  7. # return 404;
  8. #}
  9. # forward proxy for CONNECT request
  10. proxy_connect;
  11. proxy_connect_allow 443;
  12. proxy_connect_connect_timeout 10s;
  13. proxy_connect_read_timeout 15s;
  14. proxy_connect_send_timeout 15s;
  15. # forward proxy for non-CONNECT request
  16. location / {
  17. proxy_pass http://$host;
  18. proxy_set_header Host $host;
  19. }
  20. }
  21. }

使用方法

配置浏览器代理即可使用,它既能代理 HTTP 流量,也能代理 HTTPS 的流量。配置上比官方模块会更为简单。

总结

以上 2 种 https 代理都无法根据 URI 进行转发。本质上来说, Nginx 只是在 4 层转发上层的加密流量,它并没有解密 HTTPS 内容,因此无法获取 HTTP 请求时的 URI。能获取到域名,是因为 Nginx 可以通过 TLS 协议中 SNI 报文获取到域名,因此它能根据不同的域名将流量代理到不同的站点。

最终方案

用户访问的网站往往既有 HTTP 也有 HTTPS,那么我们总结一下能同时代理这 2 种流量的方案。

方案1

使用第三方开发的 ngx_http_proxy_connect_module 模块,它既支持代理 http 流量,也支持代理 https 流量。使用时让用户配置一下浏览器代理即可。

方案2

使用官方的 http 和 stream 模块分别配置 http 和 stream 的代理,即 http 模块负责代理 http 的流量,stream 负责代理 https 流量。这种方式需要用户自行修改 hosts 文件,将需要走代理的域名指向代理服务器。注意,这种方式并不支持使用浏览器代理功能。
代理 http 流量的配置如下:

  1. http {
  2. server {
  3. listen 80;
  4. resolver 114.114.114.114;
  5. location / {
  6. proxy_pass http://$http_host$request_uri;
  7. }
  8. }
  9. }

代理 https 流量的配置如下:

  1. stream {
  2. server {
  3. listen 443;
  4. resolver 114.114.114.114;
  5. ssl_preread on;
  6. proxy_connect_timeout 5s;
  7. proxy_pass $ssl_preread_server_name:$server_port;
  8. }
  9. }

用户的 hosts 文件修改如下:

  1. 192.168.0.100 foo.com
  2. 192.168.0.100 bar.com

使用时直接在浏览器访问相应域名即可。

后记

实际上,在配置代理时往往需要对来源 IP、目标 IP、目标域名等进行限制,这部分会通过单独章节进行说明。
另外,如果需要更复杂的控制,或者在 Nginx 实现更复杂的业务逻辑,可以使用 OpenResty Nginx 或者淘宝的 Tengine,它们都支持嵌入 Lua 脚本,从而能实现更复杂的控制。

参考文献