lmk123 / blog

个人技术博客,博文写在 Issues 里。
https://github.com/lmk123/blog/issues
623 stars 35 forks source link

使用 Netlify 和七牛 CDN 打造快速响应的静态网站 #94

Open lmk123 opened 2 years ago

lmk123 commented 2 years ago

在很久以前,我曾经介绍过一些免费好用的静态网站托管服务,在文章中,我最终推荐了 Netlify

后来我听说 Vercel(前身是 https://now.sh,前面的文章里也介绍过)支持的新加坡节点访问速度比 Netlify 还快,所以我将划词翻译的文档站部署到了 Vercel,但大概是在两三个月前,我发现访问速度变慢了,于是又迁回了 Netlify,结果我发现 Netlify 的访问速度也慢的不行了,打开一个网页要 3 秒甚至更长的时间。

我开始寻找其它静态网站托管服务,网上基本推荐的是 GitHub Pages、Cloudflare 或国内的腾讯云对象存储 COS 这类服务,但我最终没有使用,原因如下:

Netlify 挺好用,就是太慢了,所以只好再搭配一个 CDN 了。这样做的另一个好处是,如果以后不想用 CDN 了,那么把域名重新配置回 Netlify 即可无缝切换。

我最终选择了七牛 CDN,但文章中关于 CDN 部分的说明应该也可以应用到其它 CDN 服务上。

配置 Netlify

首先,将网站源代码上传到 GitHub 或其它 Netlify 支持的托管平台,例如划词翻译的文档站的 GitHub 地址是 https://github.com/lmk123/highlight-translator-docs

然后打开 https://app.netlify.com/ 并登录,按照提示将 GitHub 项目导入进来即可。导入完成后,Netlify 会自动打包并将项目部署到一个线上地址,例如划词翻译文档站的 Netlify 线上地址是 https://dreamy-goldstine-d64959.netlify.app/

至此,你的静态网站已经可以访问了,但是在国内访问太慢了。我们可以给一些内容不会改变的文件(例如 css、js)配置长达一年的浏览器缓存,但前提是这些文件的文件名里都根据文件内容加入了 hash,例如 main.3dfe45g.js 这类形式——不过现在已经 2021 年了,应该已经没有不这么做了静态网站了吧。

我们可以在源码根目录添加一个 netlify.toml 文件,然后写入以下配置:

[[headers]]
  for = "/*.js"
  [headers.values]
    Cache-Control = "public, max-age=31536000, must-revalidate"

[[headers]]
  for = "/*.css"
  [headers.values]
    Cache-Control = "public, max-age=31536000, must-revalidate"

[[headers]]
  for = "/*.png"
  [headers.values]
    Cache-Control = "public, max-age=31536000, must-revalidate"

这样一来,至少 js、css 和 png 文件在读取一次之后,下次加载就很快了,但是 html 文件不能缓存,所以用户查看网页时还是很慢,这个时候就轮到 CDN 出场了。

配置 CDN

由于我们已经有了一个可以访问的线上站点,那么配置 CDN 的时候就简单多了,只需要配置一个自定义域名,然后将源站设置为 Netlify 的线上访问地址即可。

但在实际使用过程中,我踩了一些坑。在介绍这些坑之前,我先简单解释一下 CDN 的工作原理,有助于理解后面的问题。

当用户访问自定义域名(例如划词翻译文档站的自定义域名是 https://hcfy.limingkai.cn)时,CDN 会先检查它自己有没有对应的文件,如果没有,它就会去 Netlify 的线上访问地址获取这个文件(这个过程称为“回源”),然后保存(或者叫缓存)下来并返回给浏览器;当用户下次再访问同样的文件时,由于 CDN 已经缓存过这个文件了,它就会直接将这个文件返回给浏览器。

好了,现在,我碰到了这些问题。

测试源站时七牛 CDN 提示检测失败

划词翻译文档站之前是直接在 Netlify 配置了 hcfy.limingkai.cn 的域名的,这次改为使用 CDN 时,我给 CDN 用的域名也是这个,但测试源站时提示测试失败。

原因是,CDN 在回源时,使用的 HTTP 请求头 Host 默认是 CDN 域名的 Host,也就是 hcfy.limingkai.cn,但由于我之前在 Netlify 配置的也是这个域名,所以回源时 Netlify 报了错。

解决方法就是在配置 CDN 回源地址时,将回源 Host 改为别的域名。

CDN 缓存设置了一年,但 Cache-Control 的 maxage 仍然是 0

在配置 CDN 的时候,七牛让我选择文件缓存时间。我给 css 文件设置了一年的缓存时间,但我在浏览器里看到的 Cache-Control maxage 仍然是 0。

后来我才意识到,CDN 的缓存时间与 Cache-Control 是两个概念。

CDN 缓存时间指的是 CDN 从源站下载下来文件后,保存到 CDN 节点中的时间。在到达这个缓存时间之前,CDN 会直接将文件返回给浏览器;在这个时间之后,CDN 会重新去源站下载文件内容。

而我看到的 Cache-Control 其实是 Netlify 设置的——CDN 会原封不动的将源站的 HTTP 响应码返回给浏览器。所以如果我们想让浏览器将文件缓存下来,就需要去 Netlify 里配置 Cache-Control。

如果你的 css、js 文件名都加入了 hash,那么也建议给 CDN 缓存配置 1 年的时间,这样的话,当一个用户在没有浏览器缓存的情况下访问 CDN 时,CDN 就不会重新去源站下载文件内容了,避免了回源所需的时间。

配置了 CDN 之后还是很慢

有以下几个原因:

URL 的尾斜线问题

划词翻译文档站有一个文件夹名叫 blog,文件夹里有 index.html 文件,以下两个 URL 都是能访问到这个文件的:

如果你同时打开这两个 URL,你会发现带尾斜线的 URL 打开速度要快得多。

这是因为,Netlify 会将不带尾斜线的 URL 301 跳转到带尾斜线的 URL 上去,而且没有办法关闭这个特性。所以,当我们访问不带尾斜线的 URL 时,流程是这样的:

  1. CDN 发现它没有缓存过 /blog 这个文件,所以它请求了 Netlify 的 /blog
  2. Netlify 返回了一个 301 到 /blog/ 的响应
  3. CDN 根据 301 的响应又重新访问了 Netlify 的 /blog/ 路径
  4. Netlify 返回了 /blog/index.html 的文件内容给 CDN,CDN 缓存了这个路径的响应
  5. CDN 将 /blog/index.html 返回给了浏览器。

在这个过程中,CDN 访问了两次 Netlify 的服务器!如果每次延迟有 3 秒,那么这个过程会花费 6 秒时间!这样的话,当你更新网站后,用户第一次访问你的网站还是很慢。

由于这个原因,所以在打包静态网站时,最好给所有目录的 index.html 访问链接都带上尾斜线,这是最通用的做法,在任何静态网站托管服务都不会出问题;在其他地方引用静态网站的链接时,也要带上尾斜线,这样打开速度会快很多。

划词翻译文档站使用的是 Docusaurus 2,它生成的链接全都是不带尾斜线的,这导致在文档站内跳转时明显很慢。不过官方已经意识到了这个问题(见 Fix trailing slash issues),应该很快就能支持生成带尾斜线的链接了。

如果你用的是其它站点生成工具,也一定要检查尾斜线是否正确生成了。

SSL 证书

使用 Netilfy 时,它会自动为我们配置的域名生成 SSL 证书并自动续期,但是在使用七牛 CDN 时,它只支持购买或自行上传 SSL 证书。

推荐使用 acme.sh 在本地生成证书并上传,如果是 Windows 系统,可以在 WSL 里使用。由于 SSL 证书 3 个月就会过期,所以我们需要每 3 个月在七牛 CDN 上传一次。

防盗链可以酌情开启

防盗链功能可以阻止其它网站引用你的网站资源,所以最好是开启的,不然其它网站引用了你的资源,支付 CDN 费用的是你自己。

但是,由于静态网站里包含了 html 文件,而我们的 html 文件肯定会被别的网站引用,所以不能开启,否则从其它网站跳转到我们的网站时会显示 403 禁止访问。

比较理想的情况是将 html 文件存放到别的服务器上,CDN 只保存 css、js 等文件,然后 CDN 就可以严格开启防盗链,但这样做比较麻烦。

最理想的情况是 CDN 服务支持单独给 html 文件配置规则,比如其它文件都开启防盗链,但 html 文件不开启。但目前为止,我没有发现提供类似功能的 CDN 服务,如果你知道的话请留言告诉我。

写在最后

这么一番步骤操作下来,总算是打通 Netlify 和 CDN 了,但这个方案仍然不是很完美,主要是前面提到的涉及到 html 文件的回源问题。只要涉及到回源,那么必定会由于 Netlify 速度太慢而导致响应速度慢。

也许更好的方案只能是将文件上传到对象存储服务来访问了 😂

s0urcelab commented 10 months ago

我很早之前也用过一段时间Netlify,但很快Netlify就被墙掉了,后来迁移到了vercel,现在也一直使用vercel,速度不能说特别快,但是也凑合。 七牛的cdn不需要备案就可以用么?

W1ndys commented 3 months ago

我很早之前也用过一段时间Netlify,但很快Netlify就被墙掉了,后来迁移到了vercel,现在也一直使用vercel,速度不能说特别快,但是也凑合。 七牛的cdn不需要备案就可以用么?

国内cdn基本都是需要备案的