87 lines
2.9 KiB
JavaScript
87 lines
2.9 KiB
JavaScript
// Return a function you can pass a Response object to and it will
|
|
// set the `Cache-Control` header.
|
|
//
|
|
// For example:
|
|
//
|
|
// const cacheControlYear = getCacheControl(60 * 60 * 24 * 365)
|
|
// ...
|
|
// cacheControlYear(res)
|
|
// res.send(body)
|
|
//
|
|
// Max age is in seconds
|
|
// Max age should not be greater than 31536000 https://www.ietf.org/rfc/rfc2616.txt
|
|
function cacheControlFactory(
|
|
maxAge = 60 * 60,
|
|
{ key = 'cache-control', public_ = true, immutable = false, maxAgeZero = false } = {}
|
|
) {
|
|
const directives = [
|
|
maxAge && public_ && 'public',
|
|
maxAge && `max-age=${maxAge}`,
|
|
maxAge && immutable && 'immutable',
|
|
!maxAge && 'private',
|
|
!maxAge && 'no-store',
|
|
maxAge >= 60 * 60 && `stale-while-revalidate=${60 * 60}`,
|
|
maxAge >= 60 * 60 && `stale-if-error=${24 * 60 * 60}`,
|
|
maxAgeZero && 'max-age=0',
|
|
]
|
|
.filter(Boolean)
|
|
.join(', ')
|
|
return (res) => {
|
|
if (process.env.NODE_ENV !== 'production' && res.hasHeader('set-cookie')) {
|
|
console.warn(
|
|
"You can't set a >0 cache-control header AND set-cookie or else the CDN will never respect the cache-control."
|
|
)
|
|
}
|
|
res.set(key, directives)
|
|
}
|
|
}
|
|
|
|
// These are roughly in order from shortest to longest
|
|
|
|
// If you do not want caching
|
|
export const noCacheControl = cacheControlFactory(0)
|
|
|
|
// Short cache for 4xx errors
|
|
export const errorCacheControl = cacheControlFactory(60) // 1 minute
|
|
|
|
// This means we tell the browser to cache the XHR request for 1h
|
|
const searchBrowserCacheControl = cacheControlFactory(60 * 60)
|
|
// This tells the CDN to cache the response for 4 hours
|
|
const searchCdnCacheControl = cacheControlFactory(60 * 60 * 4, {
|
|
key: 'surrogate-control',
|
|
})
|
|
export function searchCacheControl(res) {
|
|
searchBrowserCacheControl(res)
|
|
searchCdnCacheControl(res)
|
|
}
|
|
|
|
// 24 hours for CDN, we soft-purge this with each deploy
|
|
const defaultCDNCacheControl = cacheControlFactory(60 * 60 * 24, {
|
|
key: 'surrogate-control',
|
|
})
|
|
// Shorter because between deployments and their (sort) purges,
|
|
// we don't want the browser to overly cache because with them we
|
|
// can't control purging.
|
|
const defaultBrowserCacheControl = cacheControlFactory(60)
|
|
// A general default configuration that is useful to almost all responses
|
|
// that can be cached.
|
|
export function defaultCacheControl(res) {
|
|
defaultCDNCacheControl(res)
|
|
defaultBrowserCacheControl(res)
|
|
}
|
|
|
|
// Vary on language when needed
|
|
// x-user-language is a custom request header derived from req.cookie:user_language
|
|
// accept-language is truncated to one of our available languages
|
|
// https://bit.ly/3u5UeRN
|
|
export function languageCacheControl(res) {
|
|
defaultCacheControl(res)
|
|
res.set('vary', 'accept-language, x-user-language')
|
|
}
|
|
|
|
// Long cache control for versioned assets: images, CSS, JS...
|
|
export const assetCacheControl = cacheControlFactory(60 * 60 * 24 * 7, { immutable: true })
|
|
|
|
// Long caching for archived pages and assets
|
|
export const archivedCacheControl = cacheControlFactory(60 * 60 * 24 * 365)
|