v4.13.0
FireRedTeam/FireRedVADv4.13.0Aug 3, 2026by yusukebe
AI Summary
This release introduces first-class support for the QUERY HTTP method and delivers significant performance improvements across the core request/response path.
Key Highlights
- Core request/response path is up to 1.25x faster due to low-level optimizations.
- First-class support for the QUERY method defined in RFC 10008.
- New Method Not Allowed middleware with customizable responses.
- RegExpRouter now throws errors at registration time for misconfigured routes.
Breaking Changes
- Cache key format changed for all methods (including GET) to support QUERY caching.
- JSX `RefObject` type changed to align with React 19.
New Features
- QUERY method support across built-in middleware (Cache, ETag, CORS).
- Method Not Allowed middleware.
- Performance optimizations (header iteration, lazy allocation).
Full Release Notes
Hono v4.13.0 is now available!
The highlight of this release is performance: a batch of low-level optimizations makes the core request/response path significantly faster — up to 1.25x on common routes in our benchmark. This release also adds first-class support for the HTTP QUERY method, defined in [RFC 10008](https://www.rfc-editor.org/rfc/rfc10008.html), a new Method Not Allowed middleware, and more.
## Performance improvements
This release includes a series of small optimizations: skipping unnecessary `Headers` allocations, replacing regex tests with `indexOf`, allocating internal state lazily, and more.
Here is [`benchmarks/fetch`](https://github.com/honojs/hono/tree/main/benchmarks/fetch) comparing v4.12 and v4.13 (`ROUNDS=5 ./compare.sh`, Bun 1.4.0, Apple Silicon — each measurement runs in a fresh process, and the variant order is reversed every round to avoid warm-up bias):
| Benchmark | v4.12 | v4.13 | Speedup |
| --- | ---: | ---: | ---: |
| `ping` — `GET /` | 165.83 ns | 163.99 ns | 1.01x |
| `query` — `GET /id/1?name=bun` | 674.40 ns | 616.99 ns | **1.09x** |
| `json` — `GET /user` | 528.99 ns | 422.44 ns | **1.25x** |
| `body` — `POST /json` | 1.16 µs | 1.00 µs | **1.15x** |
The individual changes:
- perf(context): iterate the header record with `for..in` https://github.com/honojs/hono/pull/5118
- perf(url): replace regex tests with `indexOf` https://github.com/honojs/hono/pull/5121
- perf(context): skip `Headers` creation when there are no headers to merge https://github.com/honojs/hono/pull/5122
- perf(urls): refactor `tryDecodeURIComponent` https://github.com/honojs/hono/pull/5158
- perf(request): allocate `#validatedData` lazily https://github.com/honojs/hono/pull/5175
- perf(request): probe the body cache without allocating https://github.com/honojs/hono/pull/5176
In addition, the RegExpRouter rewrite described below makes route registration plus the first match roughly 20% faster.
Thanks @kibertoad for the contributions!
## First-class QUERY method support
The QUERY method — a safe, idempotent method that carries a request body — is now a first-class citizen in Hono. You can define QUERY handlers with `app.query()`:
```ts
const app = new Hono()
app.query('/search', async (c) => {
const conditions = await c.req.json()
return c.json(await search(conditions))
})
```
Thanks @shellhaki!
## QUERY support across built-in middleware
The built-in middleware has been updated to handle QUERY requests properly:
### Cache Middleware
The Cache Middleware now caches QUERY responses. Following RFC 10008 Section 2.7, the cache key incorporates a SHA-256 digest of the request content and its representation metadata, so different query bodies are cached separately:
```ts
app.query(
'/search',
cache({
cacheName: 'search-cache',
cacheControl: 'max-age=3600',
})
)
```
**Note**: To support this, the internal cache key format has changed for all methods, including GET. Cached entries are now stored under an internal URL of the form `/.hono/cache?__hono_cache_key=...`. If you purge cache entries by URL outside of the middleware (e.g. calling `caches.delete()` with the original request URL), you will need to update that logic. Existing cache entries stored with the old format will simply be re-fetched.
### ETag Middleware
The ETag Middleware now handles conditional requests for QUERY, returning `304 Not Modified` when `If-None-Match` matches.
### CORS Middleware
The CORS Middleware now includes QUERY in the default `Access-Control-Allow-Methods`, which is now `GET, HEAD, PUT, POST, DELETE, PATCH, QUERY`. If you specify `allowMethods` explicitly, nothing changes for you.
Thanks @usualoma and @Cherry!
## Method Not Allowed Middleware
The new Method Not Allowed Middleware returns a `405 Method Not Allowed` response with a proper `Allow` header when the request path matches a registered route but the method does not:
```ts
import { methodNotAllowed } from 'hono/method-not-allowed'
const app = new Hono()
app.use(methodNotAllowed({ app }))
app.get('/hello', (c) => c.text('Hello!'))
app.post('/hello', (c) => c.text('Posted!'))
// PUT /hello -> 405 Method Not Allowed
// Allow: GET, HEAD, POST
```
You can customize the response with the `onMethodNotAllowed` option:
```ts
app.use(
methodNotAllowed({
app,
onMethodNotAllowed: (c, methods) =>
c.json({ error: 'Method Not Allowed' }, 405, { Allow: methods.join(', ') }),
})
)
```
Thanks @usualoma!
## RegExpRouter throws `UnsupportedPathError` at registration time
The RegExpRouter now detects unsupported path combinations when routes are registered, instead of at the first matching request. This means misconfigured routes fail fast at startup rather than at runtime. As a bonus, registration plus the first match is roughly 20% faster.
Thanks @usualoma!
## Other improvements
- `hono/utils/headers` has been synced with the IANA HTTP Field Name Registry, adding newly registered fields such as `Accept-Query`. Thanks @akahoshi1421!
- The JWT and JWK middleware now accept a `realm` option for the `WWW-Authenticate` challenge on `401` responses, and challenge values are properly escaped. Thanks @arhxam!
- JSX: `useRef` and `RefObject` are now aligned with React 19. Note that this is a type-level change — `RefObject<T>` is now `{ current: T }`, so type a nullable ref as `RefObject<T | null>`, and pass `useRef(undefined)` instead of `useRef()`. Thanks @ashunar0!
- JSX: a function component can now return an array of children without throwing during server-side rendering. Thanks @natsuki-engr!
- The Compress Middleware now sets `Vary: Accept-Encoding` on negotiated responses. Thanks @arhxam!
## All changes
* perf(hono-base): avoid rest parameter in `fetch` by @yusukebe in https://github.com/honojs/hono/pull/5113
* perf(context): iterate the header record with for..in by @yusukebe in https://github.com/honojs/hono/pull/5118
* perf(url/request): replace regex tests with `indexOf` by @yusukebe in https://github.com/honojs/hono/pull/5121
* perf(context): skip Headers creation when there are no headers to merge by @yusukebe in https://github.com/honojs/hono/pull/5122
* perf(urls): refactor `tryDecodeURIComponent` by @yusukebe in https://github.com/honojs/hono/pull/5158
* chore(benchmarks): correct src path on Windows, add json and middleware cases by @kibertoad in https://github.com/honojs/hono/pull/5173
* perf(context): drop the throwaway `env` field initializer by @kibertoad in https://github.com/honojs/hono/pull/5174
* perf(request): allocate `#validatedData` lazily by @kibertoad in https://github.com/honojs/hono/pull/5175
* perf(request): probe the body cache without allocating by @kibertoad in https://github.com/honojs/hono/pull/5176
* chore(benchmarks): stabilize measurements by forcing mitata batching by @yusukebe in https://github.com/honojs/hono/pull/5183
* perf(hono-base): restore the rest parameter in `fetch` by @yusukebe in https://github.com/honojs/hono/pull/5184
* perf(context): restore the `env` field initializer by @yusukebe in https://github.com/honojs/hono/pull/5186
* feat: add first-class QUERY method support by @shellhaki in https://github.com/honojs/hono/pull/5070
* feat(etag): support conditional requests for the QUERY method by @Cherry in https://github.com/honojs/hono/pull/5111
* feat(cors): allow QUERY by default as a first-class method by @usualoma in https://github.com/honojs/hono/pull/5115
* feat(cache): add first-class support for QUERY requests by @usualoma in https://github.com/honojs/hono/pull/5119
* feat(jsx): add React-compatible overloads to useRef by @ashunar0 in https://github.com/honojs/hono/pull/5063
* feat(middleware): add method-not-allowed middleware by @usualoma in https://github.com/honojs/hono/pull/5132
* feat(jwt,jwk): add a configurable WWW-Authenticate realm by @arhxam in https://github.com/honojs/hono/pull/5141
* feat(utils/headers): sync HTTP field types with the IANA registry by @akahoshi1421 in https://github.com/honojs/hono/pull/5153
* fix(jsx): allow a function component to return an array by @natsuki-engr in https://github.com/honojs/hono/pull/5179
* feat(reg-exp-router): throw UnsupportedPathError during route registration by @usualoma in https://github.com/honojs/hono/pull/5171
* fix(compress): set Vary: Accept-Encoding on negotiated responses by @arhxam in https://github.com/honojs/hono/pull/5137
**Full Changelog**: https://github.com/honojs/hono/compare/v4.12.34...v4.13.0
Thank you to all contributors!