Hono 4.8–4.12 Knowledge Patch
Baseline: Hono through v4.7.x (core routing, middleware, hc, JSX/SSR, WebSocket helpers, and Cloudflare Workers/Pages, Deno, Bun, and Node.js adapters); covered range: v4.8.0 through v4.12.x plus the included topical guidance current to 2026-07-12.
Use this patch
- Read the security section before changing authentication, caching, static serving, CORS, IP restrictions, cookies, JSX/CSS SSR, or streaming.
- Read the relevant topic reference before implementing or reviewing Hono code.
- Preserve explicit JWT/JWK algorithms and the current patch-level security floor.
- Treat client path/query inputs as wire-format strings even when server validation coerces them.
- Use runtime-specific exports for adapters and static generation.
Reference index
| Reference | Topics |
| --- | --- |
| Routing and requests | HEAD dispatch, route introspection, trailing slashes, raw requests and bytes, query/body behavior, proxy headers, locale fallback, Unix sockets |
| Client, RPC, validation, and testing | hc URLs and inputs, response typing/parsing, query serialization, validator rules, Standard Schema, testClient, app.request() |
| Security and authentication | JWT/JWK algorithms and sources, Basic Auth hooks, CSP, bot blocking, security patch floors |
| Middleware, runtimes, and integrations | cache, CORS, compression, context exports, adapters, Service Workers, MCP, MIME and logging |
| Rendering, streaming, and SSG | streaming lifecycle, SSG plugins and route mapping, JSX DOM, view transitions, renderer and CSS options |
| Hono CLI | docs, search, request, serve, and optimize commands |
Breaking changes, deprecations, and security floors
Require explicit JWT algorithms
From v4.11.4 in the 4.11.0 line, configure the algorithm instead of allowing a token header to select it. jwt takes one alg; jwk takes an array of asymmetric algorithms.
import { jwk } from 'hono/jwk'
import { jwt } from 'hono/jwt'
app.use('/session/*', jwt({ secret, alg: 'HS256' }))
app.use('/admin/*', jwk({ jwks_uri, alg: ['RS256'] }))
Use v4.11.7 or newer for the 4.11 security fixes and v4.11.10 or newer for stronger timing-safe comparison. On 4.12, use at least v4.12.27 for the accumulated security fixes. See Security and authentication for the affected features.
Replace deprecated startup and SSG hooks
Start Service Worker apps with the standalone helper introduced in 4.8.0; do not add new uses of app.fire().
import { fire } from 'hono/service-worker'
fire(app)
Pass SSGPlugin objects in toSSG(..., { plugins }). Legacy SSG hook options are deprecated as of 4.9.0. Use defaultPlugin() for normal generation and put redirectPlugin() before it when generating redirect pages.
Account for changed request and route behavior
- Every
HEADrequest is converted toGETbefore matching, and its response body is removed. Put HEAD-specific work in middleware that inspectsc.req.method;app.head()andapp.on('HEAD', ...)do not run. - Multiple-handler route types now include responses from middleware and earlier handlers (since 4.10.0).
- Proxy handling strips or processes hop-by-hop headers according to RFC 9110; do not depend on those headers passing through unchanged.
- Validator body targets receive
{}when the request lacks the matchingContent-Type; header validation keys are lowercase. hcdoes not URL-encode path parameters. Encode normal values yourself, or intentionally use a regexp parameter when a raw value may span slashes.
Client and RPC quick reference
Build typed URLs and paths
Pass a literal base URL as the second hc type argument to make $url() return an exact TypedURL (since 4.11.0).
const client = hc<typeof app, 'https://api.example.com'>(
'https://api.example.com/'
)
const url = client.posts[':id'].$url({ param: { id: '42' } })
Use $path() when only a router path or cache key is needed (since 4.12.0).
const path = client.posts[':id'].$path({
param: { id: '42' },
query: { view: 'full' },
})
Use buildSearchParams on hc for custom query conventions. Keep all param and query values as strings. Per-call { init } has final precedence over the method, body, and headers generated by hc.
Type shared and status-specific responses
Use ApplyGlobalResponse to add responses from global middleware or onError() to every route schema. It is exported from hono/client from v4.12.1. Use PickResponseByStatusCode in later 4.12 releases to select a status variant. Augment NotFoundResponse when the application has a typed custom 404 body.
Parse and preserve requests
parseResponse() accepts an hc response promise, selects a parser from Content-Type, and throws DetailedError for a non-OK response (since 4.9.0).
import { parseResponse } from 'hono/client'
const data = await parseResponse(client.posts.$get())
Use cloneRawRequest(c.req) when middleware or a validator has already consumed the body and the raw Request must be sent elsewhere.
Authentication quick reference
Choose a token source deliberately
jwtacceptsheaderNamefor a nonstandard header andcookiefor a named cookie.jwkacceptsheaderName;keysandjwks_urimay be context-dependent functions.jwk({ allow_anon: true, ... })allows unauthenticated continuation and leavesjwtPayloadunset when no valid token is available.- JWT and JWK middleware accept only the
Bearerauthorization scheme.
Use JwtVariables in the application's Variables type so c.get('jwtPayload') is typed. Configure issuer validation and temporal-claim checks rather than duplicating them in handlers.
Add post-authentication work
Use Basic Auth's async-capable onAuthSuccess(c, username) hook to record identity or audit state after successful authentication, including when using verifyUser.
Middleware and runtime quick reference
Configure cache safety and variation
- Select stored statuses with
cacheableStatusCodes. - Handle unavailable runtime caches with
onCacheNotAvailable. - Configured
Varyheaders contribute to cache keys. - Responses with
Vary: AuthorizationorVary: Cookieare not cached. - Current patch releases also refuse responses marked
privateorno-store.
Select content dynamically
cors({ allowMethods }) accepts a function of the request origin. Compression accepts contentTypeFilter; start custom checks from COMPRESSIBLE_CONTENT_TYPE_REGEX. MessagePack is recognized as compressible.
Use adapter exports directly
- Import
upgradeWebSocketandwebsocketdirectly from the Bun adapter. - Import
getConnInfofrom the AWS Lambda, Cloudflare Pages, or Netlify adapter. - Configure AWS Lambda binary content types when returning binary bodies.
- Use
http+unixURLs for HTTP over Unix sockets.
Rendering, streaming, and SSG quick reference
Stop long-lived streams on abort
return streamSSE(c, async (stream) => {
while (!stream.aborted) {
await stream.writeSSE({ event: 'tick', data: 'tick' })
await stream.sleep(1000)
}
})
Producer errors after streaming begins do not reach app.onError(). Supply the streaming helper's third callback to finish or close the existing stream; it cannot replace the response. Under Wrangler, set Content-Encoding: Identity when its streaming behavior requires the workaround.
Compose the SSG plugin pipeline
import { defaultPlugin, redirectPlugin, toSSG } from 'hono/ssg'
await toSSG(app, fs, {
plugins: [redirectPlugin(), defaultPlugin()],
})
Use ssgParams() to enumerate parameterized pages, disableSSG() to exclude a route, onlySSG() for generation-only routes, and isSSGContext(c) for generation-specific output. Node.js accepts a filesystem argument; the Bun and Deno adapter entry points do not.
Protect streamed JSX
Give StreamingContext a scriptNonce and allow the same nonce in the response CSP whenever streamed Suspense or ErrorBoundary output may emit inline scripts. CSP configuration also supports report-to and report-uri.
CLI quick reference
Install @hono/cli for the hono command.
hono search "basic auth"
hono docs /docs/api/routing
hono request -P /api/users -X POST -d '{"name":"Ada"}' src/index.ts
hono serve --use 'logger()' src/index.ts
hono optimize src/index.ts
request runs app.request() in-process. serve starts at http://localhost:7070 and can inject repeated --use middleware. optimize emits a precomputed PreparedRegExpRouter entry at dist/index.js.