本文仅供学习和交流使用,搭建网站服务还请遵守当地法律法规

起因是学姐比赛临时需要起一个http服务来用,然后她的域名没备案,嫌麻烦,只能出此下策了。

为什么不直接在海外服务器上部署? 当然可以,不过也得看实际业务场景,我们大量数据都是存放在大陆数据中心的(存算资源充足)。海外服务器都是用来搭建网络节点用,有充足的带宽和流量,但是没有太多的存算资源。

你需要准备的

海外服务器/云函数/serverless等业务,未备案域名,需要备案才能访问的服务器

假设我们的未备案域名是nobeian.domain.com,有一个香港服务器5.6.7.8,一个大陆服务器1.2.3.4(解析到这个服务器的http请求均需要备案才可访问,且这几个域名均无需备案)

为什么可以这样

大陆的云服务商有各种手段拦截未备案域名的请求,注意是域名,大部分通过IP还是可以访问的。例如https下通过TLS机制中的SNI(明文)阻断,http下使用头部Host字段进行域名判断。

通常情况下,我们访问一个网站都是使用域名,然后在客户端完成DNS查询,得到域名对应的IP地址,然后客户端发起请求。

正因为请求中包含了域名/主机名的信息,所以在服务商处才可以进行拦截,我们在中转服务器上进行一些操作来偷梁换柱就可以更改这个域名信息。

http请求中的Host通常是请求库或者客户端在发送时计算的,就是域名部分,虽然Postman,Apifox这类工具和一些请求库可以自定义头部,但是你的网站是给用户用浏览器访问的,浏览器通常不允许,总不可能让用户为了访问你的网站去hack浏览器。

没有什么是加一层中间件不能解决的,如果有,那就再加一层。

场景复现

我们需要添加好解析

大部分情况下,一个服务器上有多个域名的业务时,一般不直接把业务域解析到IP,虽然这样可以降低DNS查询时间,不过也降低不了多少。用一个域名来记录主机是方便迁移,所有业务域名都CNAME到主机域名,换服务器只需要换主机域名的A/AAAA记录即可。此外,本文不讨论SSL/TLS相关内容

nobaian.com -> hk.domain.com(CNAME)

hk.domain.com -> 5.6.7.8(A)

mls.domain.com -> 1.2.3.4(A)

在香港主机上使用反代工具,例如nginx、caddy等,把所有请求都proxy_pass回源到http://mls.domain.com:80

正常情况下,你的网站应该能正常访问了,但是某些地区在这方面有一些法律法规,可能会阻断这个请求。

这时候,我们可以把Host请求头部换掉,换成已备案的域名,例如www.baidu.com(只要备案了都行,这个比较靠谱),这个域名/主机名是请求能被放行进入大陆http服务的『钥匙』

只需要一行配置(nginx)

proxy_set_header Host www.baidu.com;  # 替换备案域名
...
proxy_pass https://mls.domain.com:443  # 端口可省略

问题来了,假如我们在源服务器上部署了多个网站,也是Nginx在反代之后的,如何通过主机名区分呢,这时候Host已经变成了www.baidu.com,所有不同域名的请求的主机名都是这个,我们无法进行server_name分流。

于是我们可以在替换Host时,用一个非标自定义头部字段保存原有的Host

proxy_set_header X-Original-Host $http_host;  # 保存原始请求头
proxy_set_header Host www.baidu.com;   # 替换备案域名
...
proxy_pass http://mls.domain.com:80  # 端口可省略

有了原始主机名,我们可以在大陆服务器上的反代层再进行一次暗箱操作。

在请求进入大陆服务器后就没有备案拦截的事了,我们可以再加一层Nginx或者使用Nginx回环大法,把Host重新换回来进行server_name分流。

本文使用Nginx作为反代进行示例,在其他反代服务上操作也非常简单,且大多数反代软件或者serverless的云函数都支持。