缓存标头和利用边缘网络加速访问
本文以 Cloudflare + Vercel 为例。
了解缓存标头
在生产中,我们通常会利用 Redis 去缓存接口数据。对于前端应用来说,我们一般会利用内容分发网络(CDN)去缓存页面和其他静态资源。
我们知道 HTTP 标头 cache-control
可以用来强缓存资源。
例如,cache-control: max-age=60
表示资源在 60 秒内不会过期,浏览器如果不使用强制刷新的话(例如请求标头含有cache-control: max-age=0
)可以直接使用本地缓存。
在边缘网络或是内容分发网络中,一般都能相关的标头进行缓存。
比如:s-maxage
的值。
NoteThe s-maxage response directive indicates how long the response remains fresh in a shared cache. The s-maxage directive is ignored by private caches, and overrides the value specified by the max-age directive or the Expires header for shared caches, if they are present.
Cache-Control: s-maxage=604800
参考:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#s-maxage
而这个值,一般都被 CDN 或者边缘网络用来控制缓存时间。
在 Vercel 中。Cache 章节中,告诉我们可以使用这个值去控制一个 API 响应的缓存时间。另外也提供了 Vercel 私有的 HTTP 标头,例如 Vercel-CDN-Cache-Control
。
在 Cloudflare 中,When to use CDN-Cache-Control 中的例子可以更好的帮助理解资源在各端的缓存控制。
Headers:
Cache-Control: max-age=14400, s-maxage=84000
Cloudflare-CDN-Cache-Control: max-age=24400
CDN-Cache-Control: max-age=18000
Cache behavior:
Caches | Cache TTL (seconds) |
---|---|
Origin Server Cache | 14400 |
Network Shared Cache | 84000 |
Cloudflare Edge | 24400 |
Other CDNs | 18000 |
Browser Cache | 14400 |
上表中,Cloudflare 可以消费响应头中的 cache-control: s-maxage
和 Cloudflare-CDN-Cache-Control
控制资源在 Cloudflare 分发网和边缘网络的缓存。
通过缓存标头,可以更快的让用户访问到资源,减少服务器的负载。
以 Vercel 部署的 Next.js 为例,下面我们来做一些优化,让短期并发的负载下减少服务器的真实请求。
Next.js API Route 的响应缓存
这是一个简单的 Next.js API Route。
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export const GET = async (req: NextRequest) => {
return NextResponse.json({})
}
现在我们可以增加缓存标头。
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export const GET = async (req: NextRequest) => {
return NextResponse.json(
{},
{
headers: {
'Cache-Control': 'max-age=60, s-maxage=86400',
'CDN-Cache-Control': 'max-age=86400',
},
},
)
}
现在把 API 使用 Cloudflare 代理。
在第一次请求之后,下一次请求中,我们能看到 Cloudflare 命中了缓存。
Cf-Cache-Status: HIT
使用 vercel.json
全局配置标头
在单个 API 中,每每的添加缓存标头非常的麻烦并且我们也无法控制除了 API Route 之外的其他路由。我们可以使用 vercel.json
全局配置标头。
在项目根目录下新建 vercel.json
,内容如下:
下面路由参考项目 Shiro,可按需配置路由。
{
"headers": [
{
"source": "/",
"headers": [
{
"key": "Cache-Control",
"value": "s-maxage=300, stale-while-revalidate=59"
},
{
"key": "CDN-Cache-Control",
"value": "max-age=300"
},
{
"key": "Vercel-CDN-Cache-Control",
"value": "max-age=300"
}
]
},
{
"source": "/og",
"headers": [
{
"key": "Cache-Control",
"value": "s-maxage=3600, stale-while-revalidate=30"
},
{
"key": "CDN-Cache-Control",
"value": "max-age=3600"
},
{
"key": "Vercel-CDN-Cache-Control",
"value": "max-age=3600"
}
]
},
{
"source": "/api/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "s-maxage=60, stale-while-revalidate=30"
},
{
"key": "CDN-Cache-Control",
"value": "max-age=60"
},
{
"key": "Vercel-CDN-Cache-Control",
"value": "max-age=60"
}
]
},
{
"source": "/feed",
"headers": [
{
"key": "Cache-Control",
"value": "s-maxage=86400, stale-while-revalidate=3600"
},
{
"key": "CDN-Cache-Control",
"value": "max-age=86400"
},
{
"key": "Vercel-CDN-Cache-Control",
"value": "max-age=86400"
}
]
}
]
}
使用 Vercel 提供的配置,可以对每个路由进行缓存标头的配置。配置过后,Vercel 会在响应这些请求时,自动添加缓存标头,然后经过 Cloudflare 读取缓存标头,然后在 Cloudflare 网络进行缓存,在下次请求到达时,直接从 Cloudflare 缓存中读取。
而 stale-while-revalidate=
的值控制了一个资源在过期后,多久内仍然可以使用过期资源,然后在后台异步更新资源,那么在即便过期的场景下,用户也能快速的获取到资源,加快页面加载。
Important路由的匹配是从下到上的,那么也就是说,第一项可以定义为全局 Fallback 也就是路由匹配/(.*)
参考:https://vercel.com/docs/projects/project-configuration#headers
Vercel 边缘网缓存
你可能也注意到 Vercel-CDN-Cache-Control
这个标头。这个标头是 Vercel 私有的标头,用来控制资源在 Vercel 边缘网络的缓存。既然 Vercel 也提供了缓存,为什么还要使用 Cloudflare 呢?
我想大概有下面的原因:
- Cloudflare 一般作为 DNS 提供商,他会有着更加快速的响应时间。
- Vercel 的缓存命中很奇怪,有时候会命中,有时候不会命中,让我更费解;而 Cloudflare 的缓存命中符合文档,在意料之中。