npm / hono
hono
What changed in hono 4, from its official release notes, upgrade guides and advisories. Sources checked 2026-10-17.
20 breaking65 security6 deprecations45 new APIs and options5 performance18 behavior and defaults137 fixes and other
See which of these apply to your code, ranked, in your agent:
npx @unstale/cli inithono 4296 changes
Breaking changes 20
Rejects request paths that still contain % after decoding, preventing crafted paths from bypassing middleware mounted on a static prefix.
An invalid base64url signature now throws JwtTokenInvalid.
An empty path segment is no longer matched as a parameter.
The Cache Middleware caches QUERY responses using a SHA-256 digest of request content and representation metadata; its internal cache key URL format changes for all methods, so external cache-purge logic may need updating.
RefObject<T> is now { current: T }; type nullable refs as RefObject<T | null> and pass useRef(undefined) instead of useRef().
RegExpRouter detects unsupported path combinations during route registration, so misconfigured routes fail at startup instead of on the first matching request.
206 Partial Content responses are no longer compressed.
Any non-identity content encoding is treated as binary.
A single 0 group is no longer compressed to ::.
JWT authentication now validates the Authorization scheme and accepts only Bearer rather than any two-part header value.
JWT verify() now rejects falsy, non-finite, or non-numeric exp, nbf, and iat claims instead of allowing them to bypass time-based checks.
decode now validates token format.
decodeHeader now validates token format.
Rejects
;,\r, and\nin domain and path options to prevent cookie attribute manipulation.Rejects CR/LF characters in event, id, and retry to prevent injection of unintended SSE fields.
JWK/JWKS middleware now requires an explicit algorithm configuration, preventing untrusted JWT header values from influencing verification.
JWT middleware now requires an explicit algorithm configuration, preventing untrusted JWT header values from influencing verification.
Replaces schema-based path tracking with the
CurrentPathparameter.Deletes the NotSpecified type.
Deletes the StrictVerifyOptions type.
Security 65
Plain strings used as children or fallbacks in boundary components, or as root values in server rendering functions, are now HTML-escaped to prevent untrusted strings from being emitted as markup.
hono/jsx renders plain strings unescaped in boundary components, leading to XSS
Hono: Incomplete fix for CVE-2026-39408:
toSSG()still writes files outside the output directoryHono: Query parser reads parameters after the URL fragment, causing cache-key and proxy interpretation differentials
Hono: Unbounded dot-notation nesting in
parseBody()can cause memory exhaustionHono: Algorithmic Complexity DoS in Language Middleware
Fixes quadratic backtracking when parsing Access-Control-Request-Headers if allowHeaders is not configured, preventing long whitespace runs from stalling request processing.
Hono:
memo()retains SSR output across requests, leading to cross-user data disclosureHono: Proxy Helper does not remove response headers listed in the
ConnectionheaderHono: ReDoS in CORS middleware via Access-Control-Request-Headers
These values are parsed using Object.create(null).
Hono: API Gateway v1 adapter can drop a distinct repeated request header value during de-duplication
hono/jsx does not isolate context per request, leading to cross-request data disclosure
Hono: Server-Side XSS via JSX Escaping Bypass in cx() Utility
The API Gateway v1 and VPC Lattice adapters no longer drop a repeated header value merely because it is a substring of another value.
During SSR, context is isolated per request, so useContext() and useRequestContext() after an await in an async component no longer read another concurrent request's value.
hono: AWS Lambda adapter merges multiple
Set-Cookieheaders into one value, dropping cookies on ALB single-header and Latticehono: Body Limit Middleware can be bypassed on AWS Lambda by understating
Content-Lengthhono: CORS Middleware reflects any Origin with credentials when
origindefaults to the wildcardFixes wildcard origin reflection with credentials enabled and no explicit origin, preventing credentialed cross-origin requests from reading cookie-authenticated responses.
hono: Lambda@Edge adapter keeps only the last value of a repeated request header, dropping the rest
Fixes repeated request headers being overwritten so all values, such as those in X-Forwarded-For, reach the application.
hono: Path traversal in
serve-staticon Windows via encoded backslash (%5C)Mount-prefix stripping now uses the raw URL pathname, preventing percent-encoded paths from being routed incorrectly to the sub-application.
Hono: app.mount() strips mount prefix using undecoded path, causing incorrect routing for percent-encoded paths
Cookie helper validates sameSite and priority options against injection characters, preventing user-controlled values from injecting attributes into Set-Cookie headers.
Hono: Cookie helper does not sanitize sameSite and priority, allowing Set-Cookie injection
Hono: IP Restriction bypasses static deny rules for non-canonical IPv6
Hono: JWT middleware accepts any Authorization scheme, not only Bearer
Hono has CSS Declaration Injection via Style Object Values in JSX SSR
Hono has improper validation of NumericDate claims (exp, nbf, iat) in JWT verify()
Hono's Cache Middleware ignores Vary: Authorization / Vary: Cookie leading to cross-user cache leakage
Enforces body limits for requests without a reliable Content-Length before oversized requests can reach handlers and return successful responses.
Validates JSX tag names to prevent HTML injection when untrusted input is used as the tag name.
Hono: bodyLimit() can be bypassed for chunked / unknown-length requests
hono Improperly Handles JSX Attribute Names Allows HTML Injection in hono/jsx SSR
JSX attribute names are validated during server-side rendering to prevent malformed keys from corrupting generated HTML or injecting unintended attributes or elements.
Hono has incorrect IP matching in ipRestriction() for IPv4-mapped IPv6 addresses
Hono: Middleware bypass via repeated slashes in serveStatic
Hono missing validation of cookie name on write path in setCookie()
Hono: Non-breaking space prefix bypass in cookie name handling in getCookie()
Hono: Path traversal in toSSG() allows writing files outside the output directory
Regex metacharacters in the bearer auth prefix option are escaped.
Hono vulnerable to Prototype Pollution possible through __proto__ key allowed in parseBody({ dot: true })
When dot is enabled, parseBody ignores __proto__ path segments to prevent potential prototype pollution when merged with unsafe patterns.
Replaces regex splitting in accept handling to mitigate ReDoS.
Hono vulnerable to arbitrary file access via serveStatic vulnerability
Hono Vulnerable to Cookie Attribute Injection via Unsanitized domain and path in setCookie()
Hono Vulnerable to SSE Control Field Injection via CR/LF in writeSSE()
Fixes inconsistent URL decoding that could let protected static resources bypass route-based middleware.
Uses Math.floor instead of bitwise OR for a safe timestamp.
Hono is Vulnerable to Authentication Bypass by IP Spoofing in AWS Lambda ALB conninfo
Fixed incorrect handling of X-Forwarded-For in the AWS Lambda adapter behind ALB to prevent IP-based access control bypass.
Hono added timing comparison hardening in basicAuth and bearerAuth
Fixed a reflected XSS issue that could occur when untrusted strings were rendered without proper escaping.
Hono cache middleware ignores "Cache-Control: private" leading to Web Cache Deception
Hono has an Arbitrary Key Read in Serve static Middleware (Cloudflare Workers Adapter)
Hono IPv4 address validation bypass in IP Restriction Middleware allows IP spoofing
Hono vulnerable to XSS through ErrorBoundary component
Hono JWK Auth Middleware has JWT algorithm confusion when JWK lacks "alg" (untrusted header.alg fallback)
Hono JWT Middleware's JWT Algorithm Confusion via Unsafe Default (HS256) Allows Token Forgery and Auth Bypass
Secure headers now support CSP TrustedTypePolicy.
Hono vulnerable to Vary Header Injection leading to potential CORS Bypass
Hono Improper Authorization vulnerability
Adds RFC 7519–compliant audience validation for JWT middleware, allowing applications to reject tokens intended for other audiences.
Deprecations 6
app.mount() still works in v4 but is deprecated and will be removed in v5; use Mount Middleware with app.all() instead.
Adapter imports through hono/<adapter> still work in v4 but are deprecated and will be removed in v5; migrate imports to the separate adapter packages.
hono/cloudflare-pages is deprecated without a replacement package; Cloudflare recommends Workers with static assets.
Deprecated; use invalidAuthenticationHeader.message instead.
Deprecated; use invalidToken.message instead.
Deprecated; use noAuthenticationHeader.message instead.
New APIs and options 45
Mount Middleware is available from hono/mount as a handler that can be registered with app.all().
Set allowPercentInPath to true to serve files whose names contain a literal %.
The AWS Lambda runtime adapter is published as its own package.
The Bun runtime adapter is published as its own package, so imports can use @hono/bun.
The Cloudflare Workers runtime adapter is published as its own package.
The Deno runtime adapter is published as its own package and is also available on JSR.
The Lambda@Edge runtime adapter is published as its own package.
The Netlify runtime adapter is published as its own package.
The service-worker runtime adapter is published as its own package.
The Vercel runtime adapter is published as its own package.
The WebSocket client supports custom buildSearchParams and filters undefined query values.
Adds first-class support for defining handlers for the HTTP QUERY method.
Adds middleware that returns 405 with an Allow header when a path matches a registered route but the request method does not.
Lets you customize the response produced by the Method Not Allowed Middleware.
The JWT and JWK middleware accept a realm option for the WWW-Authenticate challenge on 401 responses, and challenge values are properly escaped.
Adds JWT and Lambda authorizer types for API Gateway v2.
COMPRESSIBLE_CONTENT_TYPE_REGEX is now re-exported.
The compress middleware adds a contentTypeFilter option.
The Context class is now publicly exported.
A new bytes() method is available on requests.
Adds the
onCacheNotAvailableoption to cache handling.Adds the
skipoption to trailing-slash handling.Adds the classNameSlug option to createCssContext.
Adds the PickResponseByStatusCode type to the client API.
Supports providing jsxRenderer options as a function.
This type helper adds global error response types to all routes in the RPC client.
The ExecutionContext type includes an exports property for Cloudflare Workers, which can be typed through module augmentation with Wrangler's generated types.
getConnInfo() is now available in the AWS Lambda, Cloudflare Pages, and Netlify adapters.
Basic Auth accepts a callback invoked after successful authentication, allowing context variables or logging without re-parsing the Authorization header; async callbacks and verifyUser mode are supported.
The Hono client adds $path(), which returns a path string with optional path and query parameters instead of a full URL.
The SSG plugin generates static HTML redirect pages for HTTP redirect responses, including a refresh tag, canonical link, and robots noindex tag.
When enabled, the middleware redirects before executing handlers, allowing trailing slash handling to work with wildcard routes.
getBunServer is now exported for Bun users.
AlgorithmTypes is exported for use by developers.
The Hono client option customizes how query parameters are serialized.
Module augmentation can customize the
NotFoundResponsetype soc.notFound()returns a typed response and the client can infer its 404 response type.The pretty-json middleware supports a
forceoption.Secure Headers adds support for the CSP
report-todirective.Secure Headers adds support for the CSP
report-uridirective.The Context Storage middleware helper returns
undefinedinstead of throwing an error when the context is not available.Hono client
$url()now returns the exact URL type, including protocol, host, and path, when the base URL is supplied as the second type parameter tohc.Adds
wrapTimeto simplify usage.AWS Lambda handling now supports AWS Lattice events.
Adds a utility to clone the raw Request object after it has been consumed by validators or middleware.
Adds a default SSG plugin that defines the recommended behavior.
Performance 5
Reduces lookup work for large keyed updates and optimizes matching-head child lookup during reconciliation.
Static array header options are pre-joined during initialization.
Low-level optimizations make common routes up to 1.25x faster in the benchmark; route registration plus the first match is roughly 20% faster.
c.json() now creates the Response directly when there is no custom status, headers, or finalized state, avoiding allocation of a Headers object.
TrieRouter performance is improved by approximately 1.5x to 2.0x across the listed route benchmarks.
Behavior and default changes 18
Media type matching supports wildcards and specificity ordering.
pretty-json supports structured JSON content types ending in +json.
Appends Origin to the Vary header on OPTIONS preflight requests.
The CORS Middleware includes QUERY in the default Access-Control-Allow-Methods list; explicitly configured allowMethods values are unchanged.
The ETag Middleware handles conditional QUERY requests and returns 304 Not Modified when If-None-Match matches.
The Compress Middleware sets Vary: Accept-Encoding on negotiated responses.
Cookie names are validated less strictly when parsing the Cookie header.
Function headers are merged with per-request headers in the client.
Charset parameters are specified per MIME type instead of being mechanically detected.
MessagePack is now a compressible content type.
Widens the children type to Child[] for jsx.
bearerAuth is generic for typed context in verifyToken, allowing token verification to use the context type.
The options parameter is now optional in all adapters, so callers can use serveStatic without passing options.
The origin option is now optional in CORSOptions.
The map now includes atom+xml and rss+xml.
normalizeLanguage uses RFC 4647 Lookup-based progressive truncation, so a locale such as ja-JP can match ja when only the base language is supported.
IsAllowedOriginHandler can now be asynchronous, allowing origin checks to complete asynchronously.
IsAllowedSecFetchSiteHandler can now be asynchronous, allowing Sec-Fetch-Site checks to complete asynchronously.
Fixes and other changes 137
JSXNode function component results are now allowed.
Internal types remain private in bundled declarations, avoiding a self-referencing JSX.IntrinsicElements type.
Numeric gridColumnGap values now receive px units.
Numeric gridGap values now receive px units.
Numeric gridRowGap values now receive px units.
The retainedHeader option correctly matches mixed-case header names.
some() now returns a Response when middleware short-circuits.
Types now allow returning a Blob as a response body.
Binary +xml archive media types are treated as binary.
Empty query parameters are preserved.
ErrorBoundary content is replaced across newlines.
Content type detection is synchronized with aws-lambda.
Malformed events fail with a descriptive error.
Media types and language tags are matched case-insensitively.
Responses with unparseable JSON bodies are no longer broken.
Suspense content is replaced across newlines.
Treats the q parameter name as case-insensitive.
Clamps a negative q value to 0 instead of 1.
Skips accept entries with quality 0 when matching.
Respects backpressure when streaming the response body.
Skips accept entries with quality 0 when matching.
Keeps the request media type when reusing a cached body.
Allows symbol keys in get and set fallbacks for Context<any>.
Keeps a parameter value of "index" in $path().
Normalizes root WebSocket URLs.
Keeps a parameter value of "index" in $url().
Abort listeners no longer crash abort().
Cached JSON bodies are serialized when cloning a raw request.
Cloned FormData requests drop stale content length.
Undefined header and cookie values are skipped.
Undefined entries inside query arrays are skipped.
Undefined entries inside form arrays are skipped.
An empty query omits the query delimiter.
ETag matching accepts optional whitespace before the comma in If-None-Match tags.
Previous ref cleanup executes when the ref prop changes on re-render.
Parameters are handled on unmatched requests.
Signed cookies with empty string values can be parsed.
Wildcard middleware is associated with matching routes.
Avoids skipping headers when filtering headers for a 304 response.
Copies pending stream bytes so they are not lost during ETag processing.
Avoids truncating embedded IPv4 addresses when expanding IPv6 addresses.
OPTIONS requests are exempt from CSRF validation.
Prevents URL corruption when replacement values contain $ replacement tokens.
Trie-router matching now handles suffix wildcard routes.
Pattern and linear routers no longer overmatch route prefixes when matching wildcard routes.
Async children of document metadata tags are rendered instead of appearing as [object Promise].
Incremental hashing for chunked responses is corrected and digests are stable across stream chunks.
The client correctly serializes multiple cookies in a request.
The client sends falsy JSON bodies.
Trailing question marks are stripped correctly for optional parameters with regex quantifiers.
Disabled Permissions-Policy directives now output standard empty parentheses instead of none.
Missing W3C Permissions-Policy directives are now supported.
ETag handling skips unsafe methods or error responses in the non-* case.
When pipe() throws, the stream utility re-acquires the writer lock.
Trie routing now counts every slash consumed by a pattern, fixing route matching.
A function component can return an array of children without throwing during server-side rendering.
HTTP field types are synced with the IANA HTTP Field Name Registry, including newly registered fields such as Accept-Query.
Fixes handling of subscription and snapshot changes for useSyncExternalStore in JSX.
CSP callbacks are kept scoped to their header.
An empty SSE id field is emitted to reset Last-Event-ID.
Fixes multipart boundary mismatches in cloned raw requests.
parseBody() reuses cached formData.
The SSE retry field is emitted when retry is 0.
Cache-Control directives are deduplicated case-insensitively.
A duplex value is set when forwarding a stream body in query mode.
replaceUrlParam no longer matches a parameter that prefixes another parameter.
The trie router matches an empty wildcard remainder after a regexp parameter.
An If-None-Match header with the value * is treated as a match.
Extra properties are stripped from array types in JSONParsed.
The handler is resolved with the value passed to the callback.
Content-encoded response bodies are base64 encoded.
V2 events are detected by request context rather than rawPath alone.
Content-Type media types are normalized for case-insensitive matching in body utilities and the validator.
Static serving now treats empty string content as found.
On Bun, WSContext.protocol now reports the requested subprotocol.
Fixed Content-Length body encoding in lambda-edge to satisfy Deno lib types.
The parser expands :: to eight zero groups.
The binary representation of the unspecified address is rendered as ::.
The missing-options error message now mentions verifyToken, making the required option clearer.
Static file paths now normalize all backslashes, not just the first.
The encoding option now respects Accept-Encoding.
The Deno WebSocket upgrade response now echoes the negotiated subprotocol.
Widens the children type to Child[] for jsxFn.
Preserves the base path of a mounted route() app.
Cache entries are keyed by configured vary headers, so requests with different values for those headers use distinct entries.
When multiple cookies have the same name, cookie lookup returns the first one.
Abort handling for streams is fixed.
Middleware response types now propagate to app.on overloads.
SVG attributes on the root svg element are now normalized.
JWT now supports single-line PEM keys.
Request processing now handles invalid header names.
The 9- and 10-handler overloads now infer the response type from the last handler.
Strong ETags are converted to weak ETags when compressing.
The select value is applied after its children are rendered.
Reflects the request origin when credentials is true and the origin is a wildcard.
Makes fire() fallback behavior consistent with handle().
Removes parseBody from bodyCache to prevent a TypeError.
Passes SSG_CONTEXT to forGetInfoURLRequest.
Input extensions are normalized to lowercase before the MIME check.
Aligns link hoisting and deduplication with React 19.
Avoids callback handler deprecation when running on NODEJS_24_X.
Fixes the “Invalid state: Controller is already closed” error in JSX.
When the path type is any, param() returns string | undefined.
Preserves the route schema in ApplyGlobalResponse.
Removes DOM type dependencies from ClientResponse and the request method.
Prevents a type diff bug when parsing form data.
Avoids mutating the options object to prevent a memory leak.
Corrects JwtVariables for ContextVariableMap.
Corrects middleware types.
Removes DOM type dependencies from ClientResponse and the request method.
Exported from hono/client so developers can use ApplyGlobalResponse from the client package.
JSX DOM rendering now handles empty arrays in the render children loop.
Client form data serialization now skips undefined values.
getPath() ignores fragment identifiers when determining the path.
Makes the auth scheme case-insensitive.
Preserves context when using await before the html helper.
writeSSE now handles \r and \r\n line endings.
The JWKS allowedAlgorithms typing is marked to let users pass a const value.
The ClientRequest type excludes $all.
Fixes middleware union type merging in MergeMiddlewareResponse.
Preserves literal union types in input type inference.
Fixed array type inference for the app.on method.
Hono JSX displays blank when children is nullish.
Fixes incorrect
apptyping with pathless handlers.Fixes incorrect path matching in the linear router.
Fixes a TypeError: null is not an object in the Bun adapter.
Adds a JSX type for hono/jsx/dom.
Fixes incomplete types for the validator.
The cache middleware skips caching when the response includes Vary: *.
Wrapped empty JSX tags are now self-closed.
Query parameter lookup now searches behind the question mark.
Preserves handler response typing in createHandlers, allowing developers to retain response types when using the helper.
The Lambda handler serves Microsoft Office files as binary.
Request ID validation accepts the = character.
Fixes types so
.useaccepts non-returning middleware created withcreateMiddleware.Corrects proxy handling of hop-by-hop headers per RFC 9110.
Middleware response types are now passed through so RPC clients can correctly infer response types.