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