v4.8.0
honojs/honov4.8.0Jun 17, 2025by yusukebe
AI Summary
Hono v4.8.0 is a feature-rich release that reduces code size (hono/tiny now ~11KB, 4.5KB gzipped) while introducing new helpers for routing introspection, enhanced JWT/CORS/JWK middleware options, JSX streaming nonce support, and a new SSG plugin system. The release also deprecates `app.fire()` in favor of a standalone `fire()` function and adds new third-party middleware packages.
Key Highlights
- Code size reduction: hono/tiny reduced by ~800 bytes to ~11KB (4.5KB gzipped)
- Route Helper functions (matchedRoutes, routePath, baseRoutePath, basePath) for route introspection
- JWT middleware now supports custom header locations beyond Authorization header
- SSG Plugin System for extending static site generation with custom functionality
- New third-party middleware: MCP, UA Blocker, and Zod v4 support
Breaking Changes
- app.fire() method is deprecated in favor of standalone fire() function from 'hono/service-worker'
New Features
- Route Helper - matchedRoutes, routePath, baseRoutePath, basePath utilities
- JWT Custom Header Location - specify any header name for token retrieval
- JSX Streaming Nonce Support - CSP-compliant nonce for inline scripts
- CORS Dynamic allowedMethods - function-based origin-specific method control
- JWK Allow Anonymous Access - allow_anon option for optional authentication
- Cache Status Codes Option - specify which status codes to cache
- Service Worker fire() function - cleaner alternative to app.fire()
- SSG Plugin System - extend generation with custom hooks
- @hono/mcp - new middleware for Model Context Protocol servers
- @hono/ua-blocker - block requests based on user agent, includes AI bot blocking
- @hono/zod-validator v4 support - Zod v4 compatibility
Full Release Notes
# Release Notes
Hono **v4.8.0** is now available!
This release enhances existing features with new options and introduces powerful helpers for routing and static site generation. Additionally, we're introducing new third-party middleware packages.
- Route Helper
- JWT Custom Header Location
- JSX Streaming Nonce Support
- CORS Dynamic allowedMethods
- JWK Allow Anonymous Access
- Cache Status Codes Option
- Service Worker `fire()` Function
- SSG Plugin System
Plus new third-party middleware:
- MCP Middleware
- UA Blocker Middleware
- Zod Validator v4 Support
Let's look at each of these.
## Reduced the code size
First, this update reduces the code size! The smallest `hono/tiny` package has been reduced by about 800 bytes from `v4.7.11`, bringing it down to approximately 11 KB. When gzipped, it's only 4.5 KB. Very tiny!
## Route Helper
New route helper functions provide easy access to route information and path utilities.
```ts
import { Hono } from 'hono'
import {
matchedRoutes,
routePath,
baseRoutePath,
basePath,
} from 'hono/route'
const api = new Hono()
api.get('/users/:id/posts/:postId', (c) => {
const matched = matchedRoutes(c) // Array of matched route handlers
const current = routePath(c) // '/api/users/:id/posts/:postId'
const base = baseRoutePath(c) // '/api' Base route path
const appBase = basePath(c) // '/api' Base path
return c.json({ matched, current, base, appBase })
})
const app = new Hono()
app.route('/api', api)
export default app
```
These helpers make route introspection cleaner and more explicit.
Thanks @usualoma!
## JWT Custom Header Location
JWT middleware now supports custom header locations beyond the standard `Authorization` header. You can specify any header name to retrieve JWT tokens from.
```ts
import { Hono } from 'hono'
import { jwt } from 'hono/jwt'
const app = new Hono()
app.use(
'/api/*',
jwt({
secret: 'secret-key',
headerName: 'X-Auth-Token', // Custom header name
})
)
app.get('/api/protected', (c) => {
return c.json({ message: 'Protected resource' })
})
```
This is useful when working with APIs that use non-standard authentication headers.
Thanks @kunalbhagawati!
## JSX Streaming Nonce Support
JSX streaming now supports nonce values for Content Security Policy (CSP) compliance. The streaming context can include a nonce that gets applied to inline scripts.
```tsx
import { Hono } from 'hono'
import {
renderToReadableStream,
Suspense,
StreamingContext,
} from 'hono/jsx/streaming'
const app = new Hono()
app.get('/', (c) => {
const stream = renderToReadableStream(
<html>
<body>
<StreamingContext
value={{ scriptNonce: 'random-nonce-value' }}
>
<Suspense fallback={<div>Loading...</div>}>
<AsyncComponent />
</Suspense>
</StreamingContext>
</body>
</html>
)
return c.body(stream, {
headers: {
'Content-Type': 'text/html; charset=UTF-8',
'Transfer-Encoding': 'chunked',
'Content-Security-Policy':
"script-src 'nonce-random-nonce-value'",
},
})
})
```
Thanks @usualoma!
## CORS Dynamic allowedMethods
CORS middleware now supports dynamic `allowedMethods` based on the request origin. You can provide a function that returns different allowed methods depending on the origin.
```ts
import { Hono } from 'hono'
import { cors } from 'hono/cors'
const app = new Hono()
app.use(
'*',
cors({
origin: ['https://example.com', 'https://api.example.com'],
allowMethods: (origin) => {
if (origin === 'https://api.example.com') {
return ['GET', 'POST', 'PUT', 'DELETE']
}
return ['GET', 'POST'] // Default for other origins
},
})
)
```
This enables fine-grained control over CORS policies per origin.
Thanks @Kanahiro!
## JWK Allow Anonymous Access
JWK middleware now supports anonymous access with the `allow_anon` option. When enabled, requests without valid tokens can still proceed to your handlers.
```ts
import { Hono } from 'hono'
import { jwk } from 'hono/jwk'
const app = new Hono()
app.use(
'/api/*',
jwk({
jwks_uri: 'https://example.com/.well-known/jwks.json',
allow_anon: true,
})
)
app.get('/api/data', (c) => {
const payload = c.get('jwtPayload')
if (payload) {
return c.json({ message: 'Authenticated user', user: payload })
}
return c.json({ message: 'Anonymous access' })
})
```
Additionally, `keys` and `jwks_uri` options now support functions that receive the context, enabling dynamic key resolution.
Thanks @Beyondo!
## Cache Status Codes Option
Cache middleware now allows you to specify which status codes should be cached using the `cacheableStatusCodes` option.
```ts
import { Hono } from 'hono'
import { cache } from 'hono/cache'
const app = new Hono()
app.use(
'*',
cache({
cacheName: 'my-cache',
cacheControl: 'max-age=3600',
cacheableStatusCodes: [200, 404], // Cache both success and not found responses
})
)
```
Thanks @miyamo2!
## Service Worker fire() Function
A new `fire()` function is available from the Service Worker adapter, providing a cleaner alternative to `app.fire()`.
```ts
import { Hono } from 'hono'
import { fire } from 'hono/service-worker'
const app = new Hono()
app.get('/', (c) => c.text('Hello from Service Worker!'))
// Use the standalone fire function
fire(app)
```
The `app.fire()` method is now deprecated in favor of this approach. Goodbye `app.fire()`.
## SSG Plugin System
Static Site Generation (SSG) now supports a plugin system that allows you to extend the generation process with custom functionality.
For example, the following is easy implementation of a sitemap plugin:
```ts
// plugins.ts
import fs from 'node:fs/promises'
import path from 'node:path'
import type { SSGPlugin } from 'hono/ssg'
import { DEFAULT_OUTPUT_DIR } from 'hono/ssg'
export const sitemapPlugin = (baseURL: string): SSGPlugin => {
return {
afterGenerateHook: (result, fsModule, options) => {
const outputDir = options?.dir ?? DEFAULT_OUTPUT_DIR
const filePath = path.join(outputDir, 'sitemap.xml')
const urls = result.files.map((file) =>
new URL(file, baseURL).toString()
)
const siteMapText = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.map((url) => `<url><loc>${url}</loc></url>`).join('\n')}
</urlset>`
fsModule.writeFile(filePath, siteMapText)
},
}
}
```
Applying the plugin:
```ts
import { toSSG } from 'hono/ssg'
import { sitemapPlugin } from './plugins'
toSSG(app, fs, {
plugins: [sitemapPlugin('https://example.com')],
})
```
Plugins can hook into various stages of the generation process to perform custom actions.
Thanks @3w36zj6!
## Third-party Middleware Updates
In addition to core Hono features, we're excited to introduce new third-party middleware packages that extend Hono's capabilities.
### MCP Middleware
A new middleware package [`@hono/mcp`](https://github.com/honojs/middleware/tree/main/packages/mcp) enables creating remote MCP (Model Context Protocol) servers over Streamable HTTP Transport. This is the initial release with more features planned for the future.
```ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StreamableHTTPTransport } from '@hono/mcp'
import { Hono } from 'hono'
const app = new Hono()
// Your MCP server implementation
const mcpServer = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
})
app.all('/mcp', async (c) => {
const transport = new StreamableHTTPTransport()
await mcpServer.connect(transport)
return transport.handleRequest(c)
})
```
Currently, this is ideal for creating stateless and authentication-less remote MCP servers.
Thanks @MathurAditya724!
### UA Blocker Middleware
The new [`@hono/ua-blocker`](https://github.com/honojs/middleware/tree/main/packages/ua-blocker) middleware allows blocking requests based on user agent headers. It includes blocking AI bots functions.
```ts
import { uaBlocker } from '@hono/ua-blocker'
import { aiBots } from '@hono/ua-blocker/ai-bots'
import { Hono } from 'hono'
const app = new Hono()
// Block specific user agents
app.use(
'*',
uaBlocker({
blocklist: ['ForbiddenBot', 'Not You'],
})
)
// Block all AI bots
app.use(
'*',
uaBlocker({
blocklist: aiBots,
})
)
// Serve robots.txt to discourage AI bots
app.use('/robots.txt', useAiRobotsTxt())
```
Thanks @finxol!
### Zod Validator v4 Support
The [`@hono/zod-validator`](https://github.com/honojs/middleware/tree/main/packages/zod-validator) middleware now supports [Zod v4](https://zod.dev/v4)!
## All Changes
* fix(etag): fallback if `res.clone()` is not supported by @yusukebe in https://github.com/honojs/hono/pull/4198
* Revert "fix(etag): fallback if `res.clone()` is not supported (#4198)" by @yusukebe in https://github.com/honojs/hono/pull/4200
* chore(devcontainer): remove obsolete version field from docker-compose.yml by @kyodaj in https://github.com/honojs/hono/pull/4208
* docs(context): fix docstring link in the set header method by @Carlos-err406 in https://github.com/honojs/hono/pull/4221
* ci: consolidate perf-measures GitHub Actions comments by @yusukebe in https://github.com/honojs/hono/pull/4222
* chore(secure-headers): format by @yusukebe in https://github.com/honojs/hono/pull/4224
* ci: add HTTP speed check by @yusukebe in https://github.com/honojs/hono/pull/4220
* ci: simplify HTTP benchmark implementation by @yusukebe in https://github.com/honojs/hono/pull/4226
* feat(middleware/cache): add `cacheableStatusCodes` option by @miyamo2 in https://github.com/honojs/hono/pull/3943
* feat(hono/jwk): Extended with `allow_anon` option & passing `Context` to callbacks by @Beyondo in https://github.com/honojs/hono/pull/3961
* fix(context): add `props` to `ExecutionContext` by @yusukebe in https://github.com/honojs/hono/pull/4030
* feat(hono/testing): Allow passing hc options to testClient by @kbrgl in https://github.com/honojs/hono/pull/4059
* feat(cors): allowedMethods by function by @Kanahiro in https://github.com/honojs/hono/pull/4060
* feat(mime): support webmanifest by @sushichan044 in https://github.com/honojs/hono/pull/4085
* feat(jsx): enable to add nonce to script tag generated by Suspense and ErrorBoundary by @usualoma in https://github.com/honojs/hono/pull/4216
* fix(utils/body): normalize key names in parseBody (#4108) by @hiroki-307 in https://github.com/honojs/hono/pull/4183
* feat(logger): support for `NO_COLOR` on cloudflare workers by @ryuapp in https://github.com/honojs/hono/pull/4094
* feat: introduce Route Helper by @usualoma in https://github.com/honojs/hono/pull/4204
* feat: support http+unix scheme by @usualoma in https://github.com/honojs/hono/pull/4148
* feat(jwt): Add custom header location by @kunalbhagawati in https://github.com/honojs/hono/pull/4218
* fix: reduce Context code by @yusukebe in https://github.com/honojs/hono/pull/4100
* fix(req): don't throw if the query param is invalid by @yusukebe in https://github.com/honojs/hono/pull/4110
* perf(trie-router): improve performance and reduce file size by @yusukebe in https://github.com/honojs/hono/pull/4217
* ci: handle fork PRs in http-benchmark workflow by @yusukebe in https://github.com/honojs/hono/pull/4229
* feat(service-worker): add `fire()` by @yusukebe in https://github.com/honojs/hono/pull/4214
* feat(hono-base): mark `app.fire()` as deprecated by @yusukebe in https://github.com/honojs/hono/pull/4231
* feat(ssg): add plugin system by @3w36zj6 in https://github.com/honojs/hono/pull/4156
* Next by @yusukebe in https://github.com/honojs/hono/pull/4227
## New Contributors
* @kyodaj made their first contribution in https://github.com/honojs/hono/pull/4208
* @Carlos-err406 made their first contribution in https://github.com/honojs/hono/pull/4221
* @miyamo2 made their first contribution in https://github.com/honojs/hono/pull/3943
* @kbrgl made their first contribution in https://github.com/honojs/hono/pull/4059
* @hiroki-307 made their first contribution in https://github.com/honojs/hono/pull/4183
* @kunalbhagawati made their first contribution in https://github.com/honojs/hono/pull/4218
* @3w36zj6 made their first contribution in https://github.com/honojs/hono/pull/4156
**Full Changelog**: https://github.com/honojs/hono/compare/v4.7.11...v4.8.0