记一次网站代理配置

最近遇到一个需要合并两个域名到一个域名下的需求,记录一下解决过程。

起源

一个项目需要和其他公司协作完成,遇到这么一个问题:页面要在对方微信中使用,并且要使用微信JS-SDK做一些操作。但微信公众平台可以配置的JS接口安全域名只有三个,而对方公司留给我们的名额只有一个,这就要求我们必须把两个域名合并成一个来提供服务。

需要合并的两个域名:

wxpage.xingshulin.com、utm.xingshulin.com

需要合并到的域名:

open.xingshulin.com

合并之后由一个域名下的两个一级目录分别提供服务:

open.xingshulin.com/wxpage、open.xingshulin.com/utm

解决思路

配置Nginx

做之前就想起之前遇到过一次类似的需求,是把 blog.xingshulin.com 合并到了 www.xingshulin.com/blog 下,是运维帮忙配置的Nginx,参考当时的配置自己配了一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
upstream com_qa-weather {
server localhost:8080 weight=1 max_fails=2 fail_timeout=30s;
}

server {
listen 80;
server_name nginx.leotian.cn;

proxy_redirect off;
server_name_in_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

location /wxpage/ {
proxy_pass http://com_qa-weather/;
}
}

上次只简单配置了 location 的 proxy_pass(location 和proxy_pass的绝对路径相对路径也是要注意的一个点,具体请看这里),也就是访问 nginx.leotian.cn 时对 localhost:8080 做反向代理,透明传输,这样配置的话,我们看下访问 nginx.leotian.cn/weather/ 请求到的是什么:

image-20190408112243555

可以看到,服务的根页面正常返回,但是根页面上请求的资源文件都是 404,因为资源文件并没有被反向代理,而我们域名的根目录下也没有 static/xx 这些资源文件,自然就404了,如果要解决这个问题,我们需要把 weather 服务的所有资源文件都放在 /weather/ 下,上次把 www.xingshulin.com/blog 代理到 blog.xingshulin.com 服务时就是这么做的,把 blog 服务的所有的资源文件都放在了一级目录 /blog 下:

image-20190408113037629

image-20190408113028325

那么如何不挪动资源文件的进行修改呢?我们增加一个 location 配置:

1
2
3
4
5
6
7
8
9
10
11
12
location / {
set $r_location "";
if ($http_referer ~* ^http?:\/\/nginx.leotian.cn\/(weather)\/) {
set $r_location $1;
}
if ($r_location) {
rewrite ^/(.*) /$r_location/$1 last;
#rewrite ^/(.*) /$r_location/$request_uri last;
}
rewrite_log on;
error_log /logs/error.log ;
}

我们看一下上面这段配置的逻辑(上面用到了 rewrite 模块的一些功能,具体使用请参考文档文章):

如果请求的 referer 包含 weather,我们就能知道这个请求应该是指向 weather 服务的,那么 rewrite URL 为 /weather/原请求地址,然后再经过第一个 location 进行反向代理,页面请求的资源文件就也可以拿到了,就不用再要求服务去把页面请求的资源文件挪动到 /weather 目录下了。配置好后我们实验一下:

image-20190408145225734

恩,页面终于正常打开了。但对 wxpage 做相同的配置:即 open.xingshulin.com/wxpage 代理到 wxpage.xingshulin.com 之后。我们再次请求,发现所有资源文件都返回200,内容也正常返回,但是页面并没有渲染出来:

image-20190402142414525

这又是什么原因呢?思考之后,我们想到应该是前端路由的问题,因为服务已经返回了全部的资源,除了ajax请求,和后端的交互应该到此为止了。接下来我们看一下前端路由出了什么问题。

路由配置

如果是后端路由,那么以上的配置应该是没有问题的,以上两个 location 已经保证我们所有请求都可以正确导向:

  • 访问 open.xingshulin.com/wxpage/xxx 页面,请求打到 wxpage.xingshulin.com/xxx

  • 页面上的资源请求,根据 referer 判断应该去哪个服务请求,从而代理到正确的资源地址

但这次要配置的两个服务都是SPA,前端路由,也就是说到了前端才判断路由或者说路由交给了前端,服务返回页面和资源文件后就不再处理路由的问题了,而增加一级目录后,访问的路径变成了:/wxpage/az,旧的前端路由列表只有 /az,并没有 /wxpage/az,所以出现了无法匹配的问题(如果前端路由写了404,就跳到404了)。

所以即使不挪动资源文件,但是写了前端路由的话,也是要对路由进行一定的修改,我们的项目目前的路由使用 vue-routeHistory 模式,配置如下(部分):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const routes = [{
path: '/az',
name: 'Home',
component: Home,
}, {
path: '/az/form-list',
name: 'FormList',
component: FormList,
}]

const router = new Router({
mode: 'history',
routes,
})

现在我们在前端进行重定向,做如下修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const wxpageRoutes = routes.map(route => ({
...route,
path: `/wxpage${route.path}`,
}))

const redirectRoutes = routes.map(route => ({
path: route.path,
redirect: {
name: route.name,
},
}))


const router = new Router({
mode: 'history',
routes: [
...wxpageRoutes,
...redirectRoutes,
],
})

上面配置的效果就是改成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const routes = [{
path: '/wxpage/az',
name: 'Home',
component: Home,
}, {
path: '/wxpage/az/form-list',
name: 'FormList',
component: FormList,
}, {
path: '/az',
redirect: {name: "Home"}
}, {
path: "/az/form-list",
redirect: {name: "FormList"}
}]

也就是说访问:/az/xxx 重定向到 /wxpage/az/xxx,这样配置之后,可以兼容之前发出去的链接,结合前面的Nginx配置,我们就实现了使用一级目录对一个域名的代理。

但是配好后刷新时候还是有点问题,其实刷新问题是 history 模式固有的问题(可以看这里了解更多)。这次我们修改路由后,也要对

总结

这次配置涉及到了对Nginx和前端路由的配置,两个配合最终实现了我们一开始的需求:合并两个域名到一个域名的一级目录下。配置过程中在调试Nginx时发现了echo模块,是在nginx程序上扩展了echo输出字符的功能,对于调试很方便。如果觉得安装模块很麻烦,再分享一个调试Nginx配置的小技巧

如果只是前端同学可能看起来有点困难,但还是建议掌握一下反向代理这个技能,毕竟现在都讲全栈开发,即便是纯前端,不懂http也只能做切图仔了。