Middleware: Cache Control
The CacheControl middleware in TriFrost helps you set explicit HTTP Cache-Control headers on your responses β making sure clients, proxies, and CDNs know exactly how to handle your content.
Whether you want to lock things down with no-store or lean into long-term public caching, this middleware gives you direct, no-magic control.
π¦ Import and Attach
import {CacheControl} from '@trifrost/core';
...
.use(CacheControl(options))
...You can apply it:
- Globally on the app
- Per router
- Per route
- As a cacheControl option when responding through
ctx.html,ctx.file,ctx.text,ctx.json
βοΈ Usage
App-wide:
app.use(CacheControl(options));Router-wide:
app.group('/static/*', router => {
router
.use(CacheControl(options))
...
})Single-Route:
router.route('/assets/*', route => {
route
.use(CacheControl(options))
...
})As part of a response:
app
.get('/assets/:path', ctx => ctx.file(`/public/${ctx.state.path}`, {cacheControl: {...}}))βοΈ Available Options
type:'no-cache' | 'no-store' | 'private' | 'public'
Sets the primary caching directive.maxage: number
Setsmax-agein seconds (e.g., 86400 = 1 day).proxyMaxage: number
Setss-maxagein seconds (for shared caches like CDNs).immutable: boolean
Marks the response as immutable (never changes).
default:falsemustRevalidate: boolean
Signals that once stale, the cache must revalidate with the origin.
default:falseproxyRevalidate: boolean
Signals that shared caches must revalidate once stale.
default:false
Note:
- Only valid type values are accepted. Invalid strings are ignored.
- maxage and proxyMaxage must be positive integers.
Examples
Public static content, cache 1 day:
app.group('/static/*', router => {
router.use(CacheControl({
type: 'public',
maxage: 86400 /* 1 day */
}));
});Header sent:
Cache-Control: public, max-age=86400
Private user dashboard, no caching:
app.group('/dashboard/*', router => {
router.use(CacheControl({
type: 'private',
maxage: 0
}));
});Header sent:
Cache-Control: private, max-age=0
Immutable build assets, cache 1 year:
app.group('/assets/*', router => {
router.use(CacheControl({
type: 'public',
maxage: 31536000, /* 1 year */
immutable: true
}));
});Header sent:
Cache-Control: public, max-age=31536000, immutable
CDN-specific shared cache control:
app.group('/cdn/*', router => {
router.use(CacheControl({
type: 'public',
maxage: 60, /* browsers: 1 min */
proxyMaxage: 600 /* shared caches: 10 min */
}));
});Header sent:
Cache-Control: public, max-age=60, s-maxage=600
Force revalidation on stale:
app.group('/reports/*', router => {
router.use(CacheControl({
type: 'private',
mustRevalidate: true
}));
});Header sent:
Cache-Control: private, must-revalidate
Using with ctx.file:
app.get('/assets/:path', ctx =>
ctx.file(`/public/${ctx.state.path}`, {
cacheControl: {
type: 'public',
maxage: 86400 /* 1 day */
}
})
);Header sent:
Cache-Control: public, max-age=86400
Using ctx.file with immutable long-term assets:
app.get('/assets/build/:path', ctx =>
ctx.file(`/public/build/${ctx.state.path}`, {
cacheControl: {
type: 'public',
maxage: 31536000, /* 1 year */
immutable: true
}
})
);Header sent:
Cache-Control: public, max-age=31536000, immutable
Best Practices
- β
Use
type: 'public'+ longmaxagefor static assets (CSS, JS, images). - β
Use
type: 'private'ortype: 'no-store'for sensitive or user-specific content. - β
Add
immutablewhen you want caches to treat assets as forever fresh. - β
Use
mustRevalidateorproxyRevalidateif you need stricter revalidation control. - β Always be explicit β donβt leave caching behavior up to defaults.