Skip to content

利用nginx反向代理拷贝网站

需求

源自一个比较奇特的需求,需要把原来的网站换个域名展示,比如用B域名显示A域名下的内容。但是B域名不能与A域名部署在同一服务器,也就是说不能在A的nginx配置中设置多域名。虽然我拥有A网站的全部代码,但是我并不想在另一台机器上部署一整套的复杂的环境。

尝试

于是乎,想到了最简单的proxy_pass。访问B域名时,直接代理到A域名。一试果然可以看到A网站的内容。可是快乐是短暂的,如果我随便点一个链接会发现又去到了A网站底下。因为许多链接的地址都是绝对地址,带着A的域名,所以虽然在B域名下显示了全部内容,但都是原样显示,意味着链接也是A网站的。

曙光的出现:sub_filter

我想到的是需要一个查找替换功能,我要把代理结果做一遍查找替换,结果一查还真有。那就是sub_filter,而且大部分nginx默认的配置都集成了这个模块,加上去看看,结果依然是老样子😭。我相信这一定是配置问题,于是发现了另一个选项sub_filter_once,这个选项是默认是on的,也就是说只替换一次,我一个页面有几百个链接,怎么可以只替换一次呢,设置 off 之后再看,所有链接都被替换,不管怎么点都没有跳出B网站,皆大欢喜。

但是以我多年踩坑的经验隐隐感到事情不会这么简单。

果然,虽然浏览都没问题,但是在我需要登录的时候再次失败。如果输入错误的密码,那么没问题,反馈你密码错误。但是输入了正确的密码之后页面闪了一下依然是未登录状态。也就是说A网站的登录接口验证成功了,但是没有正确设置到登录状态,原因很好理解,A网站设置cookie的有效域名当然是A网站了,所以B网站就不会有这个登录成功的cookie。所以依然是未登录。

新的转机:proxy_cookie_domain

虽然这需求比较奇葩,但我觉得这个问题不会是我一个人遇到,谷歌真是个好东西,当我输入proxy_pass cookie之后他就提示出了正确的搜索方式。第一条就是stackoverflow上面的相同问题,最佳答案指向proxy_cookie_domain。这简直就是为这个问题而生的。

proxy_cookie_domain的语法就是设置set-cookie中的域名,就像这样:

proxy_cookie_domain A B; //把set-cookie中的A域名换成B

又一次完美解决。撒花~~~

到这里就完整的复制了整个A网站到B域名下,而且数据同步,账号一致,可以说十分得意了。

忽然有一天

忽然有一天发现sub_filter不生效了,所有的链接都恢复了原状,最奇怪的是我没有改任何B网站的nginx配置,这让我十分头大,难道服务器还闹情绪?当然不可能,程序员应该100%相信机器是忠实的,绝对不会有闹情绪或欺骗的事情发生。

在百般搜索之下,终于发现了一个突破口,那就是压缩,因为A网站开启了gzip压缩节省流量。所以proxy_pass得到的数据也是经过了gzip压缩。压缩之后的东西当然是乱码啦,按照原来的方式查找替换就不行了。有两种方案,一种是请求A网站的时候设置proxy_set_header Accept-Encoding “”; 告诉上游不接受gzip压缩,另一种是接收gzip再解压。第一种的弊端是流量增大,代理本来就多一层转发,如果再不在流量上优化,恐怕会带来更大的延时,但是第二种方案要在nginx做一次解压不是特别容易的事。不过后来在v2ex看到了一种奇技淫巧,是个两全其美的办法>>查看原帖。

我的最终代理配置是这样的:

server {
    listen unix:/var/run/nginx-gunzip.sock;
    location / {
        proxy_set_header Accept-Encoding gzip;
        proxy_pass http://$host/$request_uri;
        gunzip on;
        access_log off;
    }
}
server {
        listen 80;
        server_name B.com;

        ……   
           
        location @master{
            sub_filter A.com B.com;
            sub_filter_once off;

            proxy_pass http://unix:/var/run/nginx-gunzip.sock:$request_uri;
            proxy_set_header Host A.com;
            proxy_set_header Accept-Encoding "";
            proxy_cookie_domain A.com B.com;
            proxy_redirect http://A.com http://B.com;
        }

这里面最底下还有一个配置没有提到的就是proxy_redirect,它与proxy_cookie_domain类似,是在返回301或302时在Location中查找替换的,具体可以查看文档。

nginx针对proxy提供了大量的配置项,满足了拷贝网站时候面临的各种问题,可以说只有想不到没有做不到了。更多反向代理的配置也请查看>>nginx官方文档

分享到:
Published in程序猿的东西

Be First to Comment

发表评论