本文以 Cloudflare + Vercel 為例。
了解快取標頭#
在生產中,我們通常會利用 Redis 去快取介面數據。對於前端應用來說,我們一般會利用內容分發網路(CDN)去快取頁面和其他靜態資源。
我們知道 HTTP 標頭 cache-control
可以用來強快取資源。
例如,cache-control: max-age=60
表示資源在 60 秒內不會過期,瀏覽器如果不使用強制刷新的話(例如請求標頭含有cache-control: max-age=0
)可以直接使用本地快取。
在邊緣網路或是內容分發網路中,一般都能相關的標頭進行快取。
比如:s-maxage
的值。
Note
s-maxage
s-maxage 回應指令表示回應在共享快取中保持新鮮的時間。s-maxage 指令會被私有快取忽略,並且會覆蓋共享快取中 max-age 指令或 Expires 標頭指定的值(如果存在)。
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
快取行為:
快取 | 快取 TTL (秒) |
---|---|
原始伺服器快取 | 14400 |
網路共享快取 | 84000 |
Cloudflare 邊緣 | 24400 |
其他 CDN | 18000 |
瀏覽器快取 | 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 的快取命中符合文檔,在意料之中。
此文由 Mix Space 同步更新至 xLog 原始鏈接為 https://innei.in/posts/tech/vercel-cloudflare-http-header-about-cache