最新消息:

Nginx反向代理使用的一些坑

技术 admin 5596浏览 0评论

在维护 jubt.net 时候(《搭建了一个聚合磁力、BT优质资源的导航站,满足日常自用需求》 ),由于一些搜索引擎被墙,为方便用户,因此通过Nginx反向代理来实现对这些网站的无缝访问。

Nginx的反向代理使用较为简单,对大部分网站直接使用proxy_pass命令即可搞定。但在使用过程中遇到一些问题。

遇到的一些典型问题:

以下 mydomain.com为 反向代理服务器,upstream.com为被代理的服务器

1、被代理的网站(上游服务器 upstream)采用301、302重定向,导致反向代理后,在客户端最终仍然跳转到原网站地址

此种情况,一些可以直接用proxy_redirect或error_page来搞定。

proxy_intercept_errors on;
error_page 301 302 307 = @handle_redirects;

location @handle_redirects {
#proxy_pass https://mydomain.com;
proxy_redirect https://upstream.com/ /;
}

可以参考:
https://stackoverflow.com/questions/20254456/intercepting-backend-301-302-redirects-proxy-pass-and-rewriting-to-another-loc
https://serverfault.com/questions/641070/Nginx-302-redirect-resolve-internally

2、一些上游服务器页面中的CSS、Javascript仍然指向原网站,导致css、js未正常代理

另外有一些网站是在页面上用JavaScript 来重定向,单纯采用proxy_redirect无法解决,此种情况可以结合sub_filter来替代页面响应内容。

针对页面内容的代理,可以使用Nginx的sub_filter的对原代理网站的页面响应内容进行替换。
sub_filter ‘upstream.com’ ‘mydomain.com’;
sub_filter_once off;

3、 启用gzip压缩导致sub_filter失效

与Nginx 启用gzip的几个相关设置值

gzip_http_version 1.0   #1.0|1.1 缺省值为1.1
proxy_http_version 1.0  #1.0|1.1 缺省值为1.0
proxy_set_header Accept-Encoding 'gzip';
gzip_proxied off

Nginx 缺省情况下以 HTTP/1.0 连接上游服务器,而 Nginx 的 gzip_http_version 缺省值是 HTTP/1.1。

gzip_http_version指令是用来设置 Nginx 启用 gzip 压缩所需的 HTTP 最低版本。也就是说缺省情况下,Nginx 连接上游服务器为不启用gzip压缩。

sub_filter 只能处理未经压缩的内容,因此启用gzip压缩后,会导致sub_filter失效。

要解决启用gzip压缩导致sub_filter的问题,有两个大的方案:

方案1:反向代理服务器对上游服务器连接请求禁用gzip压缩

通过设置

proxy_http_version 1.0

或者

proxy_set_header Accept-Encoding ''

或者

gzip_proxied off

缺点:由于gzip压缩对文本内容压缩效果明显,禁用gzip压缩后,会导致与上游服务器的流量过大。另外有一个网站会强制要求使用gzip压缩。

方案2:与上游服务器连接依然启用gzip压缩,但在调用sub_filter前先解压缩,然后再做替换操作

方案2又有几种思路:

思路1:使用Nginx ngx_http_gunzip_module

官方的 ngx_http_gunzip_module
该模块不是默认的,需要在编译时候通过指定 –with-http_gunzip_module 启用

proxy_pass https://upstream.com/;
gunzip on;
gzip_disable ".";
proxy_set_header Accept-Encoding "gzip";
sub_filter 'upstream.com' 'mydomain.com';
sub_filter_once off;

注意:此处需要设置 gzip_disable “.”; 如果不设置,sub_filter不起作用。
来源:https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1313433.html

较早还有其他一些gunzip 的方案,例如
ngx_http_gunzip_filter_module
https://github.com/huangqiheng/Nginx-gunzip

 

思路2:另一种思路比较直观,做两次proxy_pass

Nginx反向代理使用的一些坑(续)–gzip/gunzip 与sub_filter的那些事

 

思路3:使用ngx_lua的Lua脚本

可以参考:http://www.pataliebre.net/howto-make-Nginx-decompress-a-gzipped-request.html#.Vm-GOmR95GH

除了可以使用Openrestry的Lua来实现gunzip外,还可以使用Lua来修改response的header参数

 

4、一些上游服务器只支持https访问,导致反向代理无法成功

可以禁用ssl验证:

proxy_ssl_verify off;
proxy_ssl_server_name on;

5、一些上游服务器的网站会检查referer及User-Agent

proxy_set_header Referer "https://upstream.com";
proxy_set_header User-Agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0";

 

6、proxy_pass中URI参数路径带不带/问题

在proxy_pass时候,一定要注意 / 的问题,有巨大的坑。

官方说明:https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

 

proxy_pass指令中,如果目标地址中不带URI,则proxy_pass时候,对location 匹配的URI部分不做任何修改。

所谓不带URI,是指:也即proxy_pass的参数形如”http://127.0.0.1″,

例子:

location /test1/test2/ {
proxy_pass http://127.0.0.1;
}

则:
请求:http://mydomain.com/test1/test2/index.html ,经过proxy_pass 后,实际请求地址为:http://127.0.0.1/test1/test2/index.html

请求:http://mydomain.com/test1/test2/ ,经过proxy_pass 后,实际请求地址为: http://127.0.0.1/test1/test2/

proxy_pass指令中,如果目标地址中带URI,则proxy_pass时候 ,location中匹配的URI部分会被替换成 proxy_pass的参数的URI
例子:

location /test1/test2/ {
proxy_pass http://127.0.0.1/;
}

则:
请求:http://mydomain.com/test1/test2/index.html ,经过proxy_pass 后,请求地址为:http://127.0.0.1/index.html

请求:http://mydomain.com/test1/test2/ ,经过proxy_pass 后,请求地址为: http://127.0.0.1/

 

更新日志:
2020/03/24:增加使用ngx_http_gunzip_module时候,除了gunzip on ,还必须设置:gzip_disable “.”;

2020/03/12:增加 6、proxy_pass中URI参数路径带不带/问题

2020/02/25:创建初始版本

转载请注明:出家如初,成佛有余 » Nginx反向代理使用的一些坑

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址