honojs/hono · error · Error
Middleware vary configuration cannot include "*", as it disa
Error message
Middleware vary configuration cannot include "*", as it disallows effective caching.
What it means
This error is thrown by Hono's cache() middleware when the vary configuration contains the wildcard '*'. RFC 7231 Section 7.1.4 disallows '*' in the Vary header because it tells caches the response varies by unspecified request aspects, making effective caching impossible. The middleware validates directives up front and fails fast at configuration time rather than emitting an invalid header at runtime.
Source
Thrown at src/middleware/cache/index.ts:220
reportCacheNotAvailable(
options.onCacheNotAvailable,
'Cache Middleware cannot cache QUERY requests because Web Crypto is not available.'
)
}
if (options.wait === undefined) {
options.wait = false
}
const cacheControlDirectives = options.cacheControl
?.split(',')
.map((directive) => directive.toLowerCase())
const optionsVaryList = parseVaryDirectives(options.vary)
const varyDirectives = optionsVaryList.length ? new Set(optionsVaryList) : undefined
// RFC 7231 Section 7.1.4 specifies that "*" is not allowed in Vary header.
// See: https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.4
if (varyDirectives?.has('*')) {
throw new Error(
'Middleware vary configuration cannot include "*", as it disallows effective caching.'
)
}
const cacheableStatusCodes = new Set<number>(
options.cacheableStatusCodes ?? defaultCacheableStatusCodes
)
const maxQueryBodySize = options.maxQueryBodySize ?? defaultMaxQueryBodySize
const addHeader = (c: Context, responseVary: string[]) => {
if (cacheControlDirectives) {
const existingDirectives =
c.res.headers
.get('Cache-Control')
?.split(',')
// Directive names are case-insensitive (RFC 7234 §5.2); lower-case so
// the case-insensitive de-dup check below matches handler-set names
// like `Max-Age`.View on GitHub (pinned to e2740d5a1b)
Solutions
- Replace '*' with the concrete request headers the response actually depends on
- If vary comes from external input, filter out '*' (case-insensitive) before passing it
- Use the array form for clarity: vary: ['Accept-Encoding', 'Accept-Language']
- If you truly cannot enumerate the varying headers, omit vary entirely instead of using '*'
Example fix
// before
app.get('/data', cache({ cacheName: 'my-cache', vary: '*' }))
// after
app.get('/data', cache({ cacheName: 'my-cache', vary: ['Accept-Encoding', 'Accept-Language'] })) Defensive patterns
Strategy: validation
Validate before calling
const sanitizeVary = (vary: string[]): string[] =>
vary.map((d) => d.trim().toLowerCase()).filter((d) => d !== '*')
const mw = cache({ cacheName: 'x', vary: sanitizeVary(userVary) }) Type guard
const isValidVary = (vary: string[]): boolean => vary.every((d) => /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(d) && d !== '*')
Prevention
- Never pass unfiltered upstream Vary header values into options.vary
- Prefer explicit arrays of header names over comma-joined strings
- Document which request headers your cached responses actually depend on
When it happens
Trigger: Calling cache({ cacheName: 'x', vary: '*' }); building vary from user input that may contain '*'; splitting a comma string like '*, Accept-Encoding' and passing the array; copying a Vary header value from an upstream service into options.vary without filtering.
Common situations: Passing a raw header value collected from another response into vary, dynamic vary lists where '*' sneaks in, misunderstanding that the option takes concrete header names only (e.g. 'Accept-Encoding', 'Accept-Language').
Related errors
- basic auth middleware requires options for "username and pas
- bearer auth middleware requires options for "token" or "veri
- Invalid rule: ${rule}
- JWK auth middleware requires options for either "keys" or "j
- next() called multiple times
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/a0d6f21a512a6675.
Report an issue: GitHub.