代理 HTTP 流量
配置方法
代理所有流量,将所有请求原封不动转发出去。
http {
server {
listen 8080;
resolver 114.114.114.114;
location / {
proxy_pass http://$http_host$request_uri;
}
}
}
也可以根据 request uri 的不同,将请求代理到不同的后端服务器。
http {
server {
listen 8080;
resolver 114.114.114.114;
location /foo/ {
proxy_set_header Host foo.com;
proxy_pass http://192.168.0.10:80;
}
location /bar/ {
proxy_set_header Host bar.com;
proxy_pass http://192.168.0.20:80;
}
}
}
使用方法
方式1:通过 IP 地址直接访问代理,以 request uri 作为路由规则。
suhua@g7-7588:~$ curl http://192.168.0.100:8080/foo/index.html -v
* Trying 192.168.0.100...
* TCP_NODELAY set
* Connected to 192.168.0.100 (192.168.0.100) port 8080 (#0)
> GET /foo/index.html HTTP/1.1
> Host: 192.168.0.100:8080
> User-Agent: curl/7.58.0
> Accept: */*
代理 HTTPS 流量
官方模块
配置方法
使用官方的 ngx_stream_ssl_preread_module 模块,它是基于 TCP 的代理。
stream {
server {
listen 443;
resolver 114.114.114.114;
ssl_preread on;
proxy_connect_timeout 5s;
proxy_pass $ssl_preread_server_name:$server_port;
}
}
根据域名进行转发。注意,此时并不需要设置 proxy_set_header 进行 Host,可以理解为 TCP 代理 HTTPS 流量,是原封不动地转发 HTTP 流量,而 HTTP 中的 Host 并不会发生修改。
stream {
map $ssl_preread_server_name $upstream {
www.foo.com foo;
foo.com foo;
bar.cn bar;
www.bar.cn bar;
default default_upstream;
}
upstream foo {
server 192.168.0.10:443;
}
upstream bar {
server 192.168.0.20:443;
}
upstream default_upstream {
server 127.0.0.1:443;
}
server {
listen 443;
resolver 114.114.114.114;
ssl_preread on;
proxy_connect_timeout 5s;
proxy_pass $upstream;
}
}
使用方法
通过修改 hosts 文件,将域名直接指向代理服务器。
192.168.0.100 foo.com
192.168.0.100 bar.com
因为该方式流量会直接通过 TCP 穿透,所以不需要配置浏览器代理。而后端服务器一般不支持 HTTP CONNECT 连接,所以配置了浏览器代理后,反而很可能会无法工作。
第三方模块
配置方法
使用淘宝开发的 ngx_http_proxy_connect_module 模块
http {
server {
listen 8443;
resolver 114.114.114.114 ipv6=off;
resolver_timeout 5s;
#if ($host !~ '^(.*?\.)?(baidu\.com|google\.com)$') {
# return 404;
#}
# forward proxy for CONNECT request
proxy_connect;
proxy_connect_allow 443;
proxy_connect_connect_timeout 10s;
proxy_connect_read_timeout 15s;
proxy_connect_send_timeout 15s;
# forward proxy for non-CONNECT request
location / {
proxy_pass http://$host;
proxy_set_header Host $host;
}
}
}
使用方法
配置浏览器代理即可使用,它既能代理 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 流量的配置如下:
http {
server {
listen 80;
resolver 114.114.114.114;
location / {
proxy_pass http://$http_host$request_uri;
}
}
}
代理 https 流量的配置如下:
stream {
server {
listen 443;
resolver 114.114.114.114;
ssl_preread on;
proxy_connect_timeout 5s;
proxy_pass $ssl_preread_server_name:$server_port;
}
}
用户的 hosts 文件修改如下:
192.168.0.100 foo.com
192.168.0.100 bar.com
后记
实际上,在配置代理时往往需要对来源 IP、目标 IP、目标域名等进行限制,这部分会通过单独章节进行说明。
另外,如果需要更复杂的控制,或者在 Nginx 实现更复杂的业务逻辑,可以使用 OpenResty Nginx 或者淘宝的 Tengine,它们都支持嵌入 Lua 脚本,从而能实现更复杂的控制。