记录 301 重定向与 CDN 结合使用时的一个小问题
最近想跟几个不在身边的朋友玩德州扑克,可能是因为德扑跟赌博实在是分不开的缘故,我们没有找到这样的小程序,于是我就考虑自己写一个出来,也就是现在右上角的这个工具->德州扑克Demo。
开发的时候为了让前端能在任意 BASE URL 部署,我就考虑到使用 Hash 路由 + 相对路径访问后端接口,相对路径访问后端接口是这样一个原理:
- 假设当前 URL 为
/aaa/bbb
- 在 JavaScript 中调用
fetch('api/test')
,注意这里不是/api/test
。 - 那么浏览器就会调用
/aaa/api/test
。
但是这么做有一个问题,就以“德州扑克Demo”为例,如果用户当前的位置在 /tools/texas
,那么浏览器就会去 /tools/api
调用接口,如果当前用户的位置在 /tools/texas/
,那么浏览器就会去 /tools/texas/api
调用接口。
其实这个问题对于 API 来说都还不算严重,比较讨厌的是浏览器获取静态资源的情况。解决这个问题也不难,我的解决方案是增加一个拦截器,当拦截器发现用户的 Request URI 完全等于 /tools/texas
时,那就返回一个 301 让浏览器跳转到 /tools/texas/
。
本地调试的时候这种解决方法是没问题的,但是部署到生产环境就出问题了,用户访问 /tools/texas
时,收到的返回结果状态码是 200,且内容确实是 /tools/texas/index.html
(调用 /tools/texas/
时应当返回的内容),于是浏览器就高高兴兴的跑到 /tools/js/*
和 /tools/css/*
获取静态资源了,结果当然是 404,在后端打 log 发现,后端收到前端的 Request URI 是 /tools/texas/
而非 /tools/texas
。
起初我以为这是又拍云 CDN 的 bug(其实也不能排除是我 Nginx 的配置写的有问题),会在回源时给请求后面加一个 /
(大多数情况下加一个 /
影响确实不大),研究了很久都没研究明白应该怎么解决这个问题。就当我快要放弃的时候,我突然就想到了一个问题—— CDN 回源的时候也是直接向源主机发送 HTTP 请求,那么如果 CDN 服务器收到了一个 301 响应,它到底会把这个 301 原封不动的返回给客户端,还是好心的帮客户端自动完成这个跳转的过程?
于是我就去 CDN 的配置里面找,果然就让我找到了这样一条选项——
当这个选项打开时,如果 CDN 收到了 /tools/texas
返回的 301 响应,它就会直接跳转到 /tools/texas/
,并将源主机返回的 index.html
缓存在 CDN 服务器上,之后浏览器访问 /tools/texas
时,CDN 服务器就会直接返回缓存的内容,导致浏览器的相对路径不对。于是乎关掉这个选项,问题就迎刃而解了。
写这篇文章不是因为这个问题难解决,而是这样的问题原因让人意想不到。如果你也是 CDN 初学者,并且在开发 Web 应用时可能会使用到 301 状态码,那么希望这篇文章能帮助你注意到 301 状态码与 CDN 结合使用时 CDN 的工作方式,并预防一些问题的出现。