几种静态网站托管平台的 Web 服务访问特性实验对比

最近洒家的 GitHub Pro 教育优惠到期了。洒家担心无法再从私有仓库发布 GitHub Pages 网站,于是提前研究了其他几款主流的静态网站托管平台,发现它们的 Web 服务配置和 GitHub Pages 有一些差别,服务器的行为特性各不相同。于是洒家设计了一套测试样例进行了更深入的实验,简单分析了一下实验结果,供读者在选择托管平台、配置静态网站生成器时参考。

概述

/
|-- 404.html
|-- index.html
|-- test1
|   `-- index.html
|-- test1.html
|-- test2.html
|-- test3
|   `-- test3.html
`-- test4
    `-- index.html

测试结果

测试结果中,200 /index.html 表示 HTTP 状态码为 200,返回了 /index.html 的文件内容;301 --> /test1/ 表示 HTTP 重定向到 /test1/404 (default) 表示状态码为 404,返回了服务器默认错误页面,以此类推。

下列测试结果中,每个表格都会挑选一个测试对象作为标准测试对象,其他测试对象和标准测试对象对比,行为完全相同的测试点用绿色标记,行为不同但基本上可以等价替换的测试点用黄色标记(包括不同的 3xx 状态码跳转到了相同的 URL、都是 404 状态码但是响应消息正文不同等情况),行为完全不同的测试点用红色标记。红色的格子越多,和标准行为的差别越大。标准测试对象本身不做任何彩色标记。

基本测试

下表为基本测试的测试结果,以 Apache 为标准测试对象:

Apache GitHub Pages GitLab Pages Cloudflare Pages Vercel Netlify
/ 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html
/index.html 200 /index.html 200 /index.html 200 /index.html 308 --> / 200 /index.html 200 /index.html
/test1.html 200 /test1.html 200 /test1.html 200 /test1.html 308 --> /test1 200 /test1.html 200 /test1.html
/test1/ 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 301 --> /test1
/test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 200 /test1/index.html 200 /test1/index.html
/test2.html 200 /test2.html 200 /test2.html 200 /test2.html 308 --> /test2 200 /test2.html 200 /test2.html
/test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 308 --> /test3/test3 200 /test3/test3.html 200 /test3/test3.html
/test4/ 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html
/test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 200 /test4/index.html 200 /test4/index.html

完整测试

下列表格展示了完整测试的测试结果。下表以 Apache 为标准测试对象:

Apache GitHub Pages GitLab Pages Cloudflare Pages Vercel Netlify
/ 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html
/index 404 (default) 200 /index.html 200 /index.html 308 --> / 404 /404.html 301 --> /
/index.html 200 /index.html 200 /index.html 200 /index.html 308 --> / 200 /index.html 200 /index.html
/test1 301 --> /test1/ 200 /test1.html 302 --> /test1/ 200 /test1.html 200 /test1/index.html 200 /test1.html
/test1.html 200 /test1.html 200 /test1.html 200 /test1.html 308 --> /test1 200 /test1.html 200 /test1.html
/test1/ 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 301 --> /test1
/test1/index 404 (default) 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 404 /404.html 301 --> /test1/
/test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 200 /test1/index.html 200 /test1/index.html
/test2 404 (default) 200 /test2.html 200 /test2.html 200 /test2.html 404 /404.html 200 /test2.html
/test2/ 404 (default) 404 /404.html 200 /test2.html 308 --> /test2 404 /404.html 301 --> /test2
/test2.html 200 /test2.html 200 /test2.html 200 /test2.html 308 --> /test2 200 /test2.html 200 /test2.html
/test2.html/ 404 (default) 404 /404.html 200 /test2.html 404 /404.html 200 /test2.html 301 --> /test2
/test3 301 --> /test3/ 301 --> /test3/ 302 --> /test3/ 404 /404.html 404 /404.html 404 /404.html
/test3/ 200 (Index of /test3) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test3/test3 404 (default) 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 404 /404.html 200 /test3/test3.html
/test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 308 --> /test3/test3 200 /test3/test3.html 200 /test3/test3.html
/test4 301 --> /test4/ 301 --> /test4/ 302 --> /test4/ 308 --> /test4/ 200 /test4/index.html 301 --> /test4/
/test4/ 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html
/test4/index 404 (default) 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 404 /404.html 301 --> /test4/
/test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 200 /test4/index.html 200 /test4/index.html
/404 404 (default) 200 /404.html 200 /404.html 200 /404.html 404 /404.html 200 /404.html
/404.html 200 /404.html 200 /404.html 200 /404.html 308 --> /404 200 /404.html 200 /404.html
/xx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/ 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test1/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html

下表以 GitHub Pages 为标准测试对象:

Apache GitHub Pages GitLab Pages Cloudflare Pages Vercel Netlify
/ 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html
/index 404 (default) 200 /index.html 200 /index.html 308 --> / 404 /404.html 301 --> /
/index.html 200 /index.html 200 /index.html 200 /index.html 308 --> / 200 /index.html 200 /index.html
/test1 301 --> /test1/ 200 /test1.html 302 --> /test1/ 200 /test1.html 200 /test1/index.html 200 /test1.html
/test1.html 200 /test1.html 200 /test1.html 200 /test1.html 308 --> /test1 200 /test1.html 200 /test1.html
/test1/ 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 301 --> /test1
/test1/index 404 (default) 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 404 /404.html 301 --> /test1/
/test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 200 /test1/index.html 200 /test1/index.html
/test2 404 (default) 200 /test2.html 200 /test2.html 200 /test2.html 404 /404.html 200 /test2.html
/test2/ 404 (default) 404 /404.html 200 /test2.html 308 --> /test2 404 /404.html 301 --> /test2
/test2.html 200 /test2.html 200 /test2.html 200 /test2.html 308 --> /test2 200 /test2.html 200 /test2.html
/test2.html/ 404 (default) 404 /404.html 200 /test2.html 404 /404.html 200 /test2.html 301 --> /test2
/test3 301 --> /test3/ 301 --> /test3/ 302 --> /test3/ 404 /404.html 404 /404.html 404 /404.html
/test3/ 200 (Index of /test3) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test3/test3 404 (default) 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 404 /404.html 200 /test3/test3.html
/test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 308 --> /test3/test3 200 /test3/test3.html 200 /test3/test3.html
/test4 301 --> /test4/ 301 --> /test4/ 302 --> /test4/ 308 --> /test4/ 200 /test4/index.html 301 --> /test4/
/test4/ 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html
/test4/index 404 (default) 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 404 /404.html 301 --> /test4/
/test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 200 /test4/index.html 200 /test4/index.html
/404 404 (default) 200 /404.html 200 /404.html 200 /404.html 404 /404.html 200 /404.html
/404.html 200 /404.html 200 /404.html 200 /404.html 308 --> /404 200 /404.html 200 /404.html
/xx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/ 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test1/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html

结论

在最普通的 Web 服务的基础上,各个平台都加上了一些新的访问和跳转规则。测试结果反映出各个平台实现特性的逻辑和规则的优先级有一些区别(例如 /test1 既可以返回 /test1.html,又可以跳转到 /test1/,还可以直接返回 /test1/index.html)。洒家横竖看了半天,发现想做个简单易懂且全面的总结并不简单,无论用文字还是伪代码都只会越搞越乱,因此只能粗略总结一下,建议有兴趣的读者直接看原始结果。

各个静态网站托管平台的特性支持情况如下:

在 Cloudflare Pages 平台,访问所有 /path/to/index.html 都会 308 跳转到 /path/to/,访问其他 .html 文件也会通过 308 跳转去掉 .html 扩展名。根据洒家的理念,静态网站应该保持最大的兼容性,而不依赖任何平台的特性。无论把代码上传到任何平台、任何服务器上都应该可以正常访问,甚至直接在本地通过 file:// 协议打开也应该能正常访问。由于这种强制跳转特性,洒家不会选择 Cloudflare Pages 平台。

而 Netlify 平台在基本测试中也存在瑕疵。同时存在 /test1/index.html/test1.html 的情况下,访问 /test1/ 会先 301 跳转到 /test1,然后 200 返回 /test1.html 的内容,这和一般的预期不同。访问 /test1 的结果可以存在争议,而访问 /test1/ 则应该是想访问 /test1/index.html。再观察 /test2/ 的访问结果,在存在 /test2.html 的情况下,访问 /test2/301 跳转到 /test2,猜测访问 /test1/ 出现的现象可能是由于这个特性的优先级过高。

在 Vercel 平台,存在 /test4/index.html 的情况下,访问 /test4 没有跳转到 /test4/,而是直接 200 返回了 /test4/index.html。同时,我们又可以直接访问 /test4/index.html。同时存在两种路径层级的访问方式,这可能导致页面资源相对引用混乱。

从基本测试可以看出,只有 GitHub Pages、GitLab Pages 和 Vercel 符合洒家的基本标准。从完整测试可以看出,最接近 Apache 的是 Vercel,最接近 GitHub Pages 的则是 GitLab Pages。因此,洒家可能会用 GitLab Pages 或者 Vercel 替代 GitHub Pages。然而到最后洒家忙活的这一圈也没用上,因为洒家另外搞出了一套方案,改用 GitHub Actions 从公开仓库私密发布 GitHub Pages 网站,不用再换平台了。感兴趣的读者可以前往仓库 Phuker/phuker.github.io 查看这套方案。


Comments

您可以匿名发表评论,无需登录 Disqus 账号,勾选“我更想匿名评论”后,姓名和电子邮件分别填写“匿名”和“someone@example.com”然后发表评论即可。您也可以登录 Disqus 账号后发表评论。您的评论可能需要经过我审核后才能显示。点赞投票按钮(Reactions)无需登录即可点击。Disqus 评论系统在中国大陆可能无法正常加载和使用。

License

Creative Commons License

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).

Top