解决 sub_filter 不能替换 Gzip 过的内容

2020-11-24T16:33:35.000Z
9

在微信小程序 web-view (opens new window) 跳转外部链接?甲方的需求千奇百怪,好在办法总比困难多。不是只允许业务域名吗,不大可能在别人网站放一个 txt 校验吧?此时反代不失为解决业务域名限制的一个办法,总之一切为目标服务。

⚠️ 就算绕开了限制,由于内容经你方服务器转发,你方也须承担责任。另请注意,撰文时个人类型的小程序暂不支持内嵌网页。本文假设我方域名 yourdomain.com,对方域名 example.com。

1) proxy_pass 转发下?感觉事情没那么简单。

location / {
    ...
    proxy_pass https://example.com;
    proxy_set_header Host example.com;
    proxy_set_header Referer https://example.com;
    ...
}
  1. 尝试修改绝对 URL,将对方域名变成我方域名,避免打开非业务域名。
location / {
    ...
    proxy_set_header Accept-Encoding '';

    sub_filter_types *;
    sub_filter_once off;
    sub_filter 'example.com' 'yourdomain.com';
    ...
}
  1. 有些网站无视 Accept-Encoding: '',不管三七二十一,统统返回压过的。sub_filter 又只能处理未经压缩的内容,所以替换失败。

先来看人家怎么办到的。第一个 location / 是对方 frontend 端的配置,向 upstream 请求压缩过的资料;第二个 location / 则是对方 backend 端的配置,开启 Gzip,并允许在 HTTP/1.0 压缩。

location / {
    ...
    proxy_set_header Accept-Encoding 'gzip';
    ...
}

前文提到,Nginx 默认以 HTTP/1.0 连接上游 (opens new window),偏偏 gzip_http_version 缺省值是 1.1,表现为对后端不启用 Gzip,所以…

location / {
    ...
    gzip on;
    gzip_http_version 1.0;
    ...
}
  1. 思路清晰了有没有?先解压、再替换就好了呗。前人留下许多牛掰的方法。Maxim Dounin 出于减省回源带宽的考量,写了个 gunzip filter (opens new window) 模块。姚伟斌又撸了个 patch (opens new window),使 upstream 返回内容总是被解压 (opens new window)。还有人直接修改 Nginx 源码 (opens new window)以达到目的。

另一种 trick (感谢 ryd994 大大的原帖 (opens new window)) 比较直观。做两次 proxy_pass,先反代源站 gzip 过的内容,再反代一次即可获得未压缩的内容。这里假设第一次的 location 在 forward 目录,第二次的在根目录。

⚠️ 上面的 gunzip on 和下面的 proxy_set_header Accept-Encoding '' 互为替代。

location /forward {
    ...
    proxy_pass https://example.com;
    # proxy_set_header Accept-Encoding 'gzip';
    # gunzip on;
    ...
}

location / {
    ...
    proxy_pass https://yourdomain.com/forward;
    proxy_set_header Accept-Encoding '';

    sub_filter_types *;
    sub_filter_once off;
    sub_filter 'example.com' 'yourdomain.com';
    ...
}

⚠️ 即使您在 Step 2 透过禁用压缩的方式顺利替换,也可以试看看 Step 4。这种需求很能吃出站流量,带宽嘛,总是省一点算一点。

微雨众卉新,一雷惊蛰始。