记一次反向代理的搭建

Jul 4, 2019
2028
#反向代理#Startpage#搜索引擎

本文最近一次更新于 4 年 5 个月前,其中的内容很可能已经有所发展或是发生改变。

https://ae01.alicdn.com/kf/HTB1Mtzwe.uF3KVjSZK9762VtXXa2.png

最近和朋友聊天时,他说发现了一个云计算服务商:「ZEIT」,可以为程序免费提供托管,想问问我有没有什么好的想法。我查看了一下发现这货在全球提供的线路还真不少(亚洲大部分地区都有),用作代理软件想必体验会比较不错。

于是我首先想到搭建 ShadowSocks、V2Ray 这类代理软件,不过很可惜 ZEIT 在部署上有限制:不支持 WebSocket(当然更不支持 SOCKS5 了),且虽然线路的延迟比较低,但在带宽上却有限制,于是我暂时放弃了正向代理,把注意打到了反向代理的头上。

反向代理相比正向代理的限制不少,最大限制的在于一次只能代理一个网站。想了想还是决定代理一个搜索引擎——Startpage,用于手机等不那么方便翻墙的设备搜索。至于为什么选择它嘛,有两点原因:

  1. 它是一个非常「干净」的搜索引擎,隐私性做得非常好,甚至没有登录功能,用户偏好也只是用 Cookie 实现;
  2. 它匿名地向 Google 提交查询,再将结果返回给用户(某种意义上来说,它也算 Google 的反向代理),所以搜索质量约等于 Google。

至于为什么不选择 Google,答案也很简单,Google 会检测计算机的异常流量,一旦检测到异常,则必须通过「reCAPTCHA」检测才能继续使用。尤其是在使用反代的时候,出现检测的机率非常高,这对想迅速得到搜索引擎反馈的用户来说,无疑是一种灾难。

时隔一年,我真香了,在把本站的代理切换成 Google 一段时间之后访问时也并没有出现「reCAPTCHA」检测,因此本站会继续保持代理 Google。

⚠️:目前,我的 ZEIT 账户因为搭建的代理被太多人次访问(每个月差不多使用了 100g 的流量),已经遭到官方永久性冻结账户了。所以大家还是尽量自己搭建自己使用吧。

思路

反向代理的核心思路或者说原理其实很简单:中转服务器把来自客户端的请求发送给服务端,再将服务端的应答返还给客户端。单纯地实现这一功能也非常简单,使用 Golang,你甚至不需要借助第三方库便可搭建一个很简单的反向代理。

func Proxy(w http.ResponseWriter, r *http.Request) {
    reverseURL, err := url.Parse(protocal + host)
    proxy := httputil.NewSingleHostReverseProxy(reverseURL)
    r.Host = host
    proxy.ServeHTTP(w, r)
}

这几行代码就足以搞定 Google 的反向代理了,也能让你愉快地使用 DuckDuckGo 了,然而却无法使用 Startpage。

是的,可能因为「Startpage」本身就相当于对 Google 的反代,所以它对反向代理极其不友好,具体见下。

薛定谔的 Bug 们

绝对路径

当我运行刚刚的程序,打开浏览器并输入地址满心欢喜地看着 Startpage 的首页一点点出现时,我几乎以为已经成功了,可是当我输入关键字搜索时,打开 Firefox 的调试工具却发现它的请求资源都是从「Startpage」域名返回的。

是的,Startpage 很「聪明地」将静态文件的引用使用了绝对路径,而不是大多数网站都使用的相对路径,这意味着我还需要修改 Response Body,将原域名都替换成自己的域名,这一点倒是没什么难度(当时我是这么想的),正好 Golang 也提供了 ModifyResponse 用于 Response 的修改。

可当我代码写好了然后发现运行结果仍和原来一样时,我开始觉得有点难办了。

传输编码

造成这个问题的原因其实很明显,但我却花了半天的时间才找到:Startpage 在网页传输时启用了 GZIP 的压缩编码,因此直接替换 www.startpage.com 是行不通的,需要将 Response Body 解码之后再替换。

完成解码替换之后,终于如愿看到请求资源都是从本域名返回的,我又一次以为自己要成功了。可是当我点击搜索结果的下一页时,网页却久久处于加载之中,我开始觉得或许不应该选择代理「Startpage」了。

域名改变

打开 Firefox 的调试工具之后,发现它居然把第二页的域名给换成了与首页完全不同的二级域名——「www」会变成类似「s2-us8」、「s3-us6」这的前缀,而具体变成什么样是由首次搜索的时候随机返回的。

这个问题其实应该是无解的——除非你把所有出现的二级域名都进行代理(类似 YouTube 其实也是把每个视频的源文件放在不同域名的服务器上),不过很可惜,Startpage 只是单纯把域名换了,实测之后直接输入域名前缀也是可以正常使用的,因此只需要把代理的 URL 从 www.startpage.com 换成 s3-us6.startpage.com 就可以加载后几页的内容了。

处理 Header

在修复了以上三个 Bug 之后,搜索功能已经很完善了,不过还有一个小问题,就是用户的偏好设置无法保存,比如自定义背景、偏好语言等,点击保存按钮会 301 重定向至 www.startpage.com 页面,是的,这又是「Startpage」为反向代理设置的一道关卡。(没办法,自己选的路,哭着也要走完 : )

在修改完 301 的重定向地址后,点击保存发现虽然不会跳转到「Startpage」域名了,但是设置依然没有保存下来,一番 Debug 后发现是 Cookie 的问题,Cookie 设置的 Domain 不是同样使用的是绝对路径,「Startpage」为了不让别人反代真是煞费苦心呐!

在直接把 Cookie 的 Domian 字段干掉之后,使用起来终于和原网站无异了。

总结

虽然一开始只想着反代一个搜索引擎,但中途想着还是把普适性做得更广一些,让它能代理任意的网站,因此考虑的方面也比较多,但最终的成就感还是挺爽的,自己也对 HTTP 各字段的理解更深刻了。

目前这个反向代理工具支持文本替换、重定向替换、Cookie 替换等,源码已开源在 GitHub,部署在 ZEIT 上,如果你想部署在自己的服务器上,建议使用 master 分支。

参考:

记一次反向代理的搭建

https://blog.itswincer.com/posts/1352252a/

作者

Wincer

更新于

Jun 6, 2020

许可协议

CC BY-NC-ND 4.0
  1. Mar 4, 2024

    基于 OpenCL 生成 Solana 虚荣地址
  2. Nov 12, 2023

    Quantumult X 的链式代理配置
  3. Mar 14, 2023

    ChatGPT 与 TTS 之间奇妙的反应
  4. May 5, 2021

    Type-Length-Value Encoding Scheme Practice
  5. Jul 8, 2020

    基于 ETS 的漏斗限流
  6. Apr 22, 2020

    使用 OpenCore 引导黑苹果踩坑记录