JWT realm option
The realm option (optional, string) specifies the protection space described by the realm parameter of the WWW-Authenticate challenge header returned on 401 responses. The default is the request URL. Example: realm: 'my-protected-api'
JWT verification option structure
The verification option (optional, VerifyOptions) controls verification of the token with the following sub-options: iss (string | RegExp) for expected issuer, aud (string | string[] | RegExp) for expected audience, nbf (boolean, default true) to verify not-before claim, iat (boolean, default true) to verify issued-at claim, exp (boolean, default true) to verify expiration claim.
JWT iat claim verification default
The iat (issued at) claim will be verified if present and verification.iat is set to true. The default is true.
JWT nbf claim verification default
The nbf (not before) claim will be verified if present and verification.nbf is set to true. The default is true.
JWT Authorization header format requirement
The Authorization header sent from the client must include a specified scheme, such as 'Bearer my.token.value' or 'Basic my.token.value'.
JWT middleware with dynamic secret from environment
To use an environment variable for the secret (e.g., c.env.JWT_SECRET), wrap the jwt() middleware in a custom middleware function that creates the jwt middleware with the dynamic secret: app.use('/auth/*', (c, next) => { const jwtMiddleware = jwt({ secret: c.env.JWT_SECRET, alg: 'HS256' }); return jwtMiddleware(c, next); })
JWT Auth Middleware basic usage
The jwt() middleware requires two options: secret (string) and alg (string algorithm type). It checks for an Authorization header by default if the cookie option is not set. Example: app.use('/auth/*', jwt({ secret: 'it-is-very-secret', alg: 'HS256' }))
JWK Auth Middleware basic usage
Example of basic jwk() middleware usage:
const app = new Hono()
app.use(
'/auth/*',
jwk({
jwks_uri: `https://${backendServer}/.well-known/jwks.json`,
alg: ['RS256'],
})
)
app.get('/auth/page', (c) => {
return c.text('You are authorized')
})
JWK Auth Middleware overview
The JWK Auth Middleware authenticates requests by verifying tokens using JWK (JSON Web Key). It checks for an Authorization header and other configured sources such as cookies if specified. It validates tokens using provided keys, retrieves keys from jwks_uri if specified, and supports token extraction from cookies if the cookie option is set.
JWK Auth Middleware validation checks
For each token, jwk() performs: parsing and validating the JWT header format; requiring a kid header and finding a matching key by kid; rejecting symmetric algorithms (HS256, HS384, HS512); requiring the header alg to be included in the configured alg allowlist; if a matched JWK has an alg field, requiring it to match the JWT header alg; verifying the token signature with the matched key; by default, validating time-based claims (nbf, exp, and iat). Optional claim validation can be configured with the verification option: iss validates issuer when provided; aud validates audience when provided.
Authorization header scheme requirement
The Authorization header sent from the client must have a specified scheme. Examples are Bearer my.token.value or Basic my.token.value.
JWK Auth Middleware import
Import with: import { Hono } from 'hono' and import { jwk } from 'hono/jwk' and import { verifyWithJwks } from 'hono/jwt'.
JWK Auth Middleware get payload
Access JWT payload in a handler with c.get('jwtPayload'). Example:
const app = new Hono()
app.use(
'/auth/*',
jwk({
jwks_uri: `https://${backendServer}/.well-known/jwks.json`,
alg: ['RS256'],
})
)
app.get('/auth/page', (c) => {
const payload = c.get('jwtPayload')
return c.json(payload) // eg: { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
})
JWK Auth Middleware allow anonymous access
To allow anonymous access, set allow_anon to true and use c.get('jwtPayload') to check if authenticated. Example:
const app = new Hono()
app.use(
'/auth/*',
jwk({
jwks_uri: (c) =>
`https://${c.env.authServer}/.well-known/jwks.json`,
alg: ['RS256'],
allow_anon: true,
})
)
app.get('/auth/page', (c) => {
const payload = c.get('jwtPayload')
return c.json(payload ?? { message: 'hello anon' })
})
verifyWithJwks utility function
The verifyWithJwks utility function can be used to verify JWT tokens outside of Hono's middleware context, such as in SvelteKit SSR pages or other server-side environments. Example:
const id_payload = await verifyWithJwks(
id_token,
{
jwks_uri: 'https://your-auth-server/.well-known/jwks.json',
allowedAlgorithms: ['RS256'],
},
{
cf: { cacheEverything: true, cacheTtl: 3600 },
}
)
JWK Auth Middleware JWKS fetch request options
To configure how JWKS is retrieved from jwks_uri, pass fetch request options as the second argument of jwk(). This argument is RequestInit and is used only for the JWKS fetch request. Example:
const app = new Hono()
app.use(
'/auth/*',
jwk(
{
jwks_uri: `https://${backendServer}/.well-known/jwks.json`,
alg: ['RS256'],
},
{
headers: {
Authorization: 'Bearer TOKEN',
},
}
)
)
JWK Auth Middleware options reference
jwk() options:
1. alg (required): string[] - An array of allowed asymmetric algorithms used for token verification. Available types are RS256 | RS384 | RS512 | PS256 | PS384 | PS512 | ES256 | ES384 | ES512 | EdDSA.
2. keys (optional): HonoJsonWebKey[] | (c: Context) => Promise<HonoJsonWebKey[]> - The values of your public keys, or a function that returns them. The function receives the Context object.
3. jwks_uri (optional): string | (c: Context) => Promise<string> - If this value is set, attempt to fetch JWKs from this URI, expecting a JSON response with keys, which are added to the provided keys option. You can also pass a callback function to dynamically determine the JWKS URI using the Context.
4. allow_anon (optional): boolean - If this value is set to true, requests without a valid token will be allowed to pass through the middleware. Use c.get('jwtPayload') to check if the request is authenticated. The default is false.
5. cookie (optional): string - If this value is set, then the value is retrieved from the cookie header using that value as a key, which is then validated as a token.
6. headerName (optional): string - The name of the header to look for the JWT token. The default is Authorization.
7. realm (optional): string - The protection space described by the realm parameter of the WWW-Authenticate challenge header returned on 401 responses. The default is the request URL.
8. verification (optional): VerifyOptions - Configure claim validation behavior in addition to signature verification.
JWK Auth Middleware VerifyOptions reference
VerifyOptions object for jwk() verification option:
1. iss (optional): string | RegExp - The expected issuer used for token verification. The iss claim will not be checked if this isn't set.
2. aud (optional): string | string[] | RegExp - The expected audience used for token verification. If this is set, the token must include an aud claim and at least one audience value must match.
3. nbf (optional): boolean - The nbf (not before) claim will be verified if present and this is set to true. The default is true.
4. iat (optional): boolean - The iat (issued at) claim will be verified if present and this is set to true. The default is true.
5. exp (optional): boolean - The exp (expiration time) claim will be verified if present and this is set to true. The default is true.
logger middleware logs outgoing response
The logger middleware logs the HTTP method, request path, response status code, and request/response times for each response.
logger middleware basic usage
Call app.use(logger()) to apply the logger middleware to log all incoming and outgoing requests.
logger middleware import
Import the logger middleware from 'hono/logger'.
logger middleware custom PrintFunc example
Example of setting up a custom PrintFunc: export const customLogger = (message: string, ...rest: string[]) => { console.log(message, ...rest) }; then call app.use(logger(customLogger)). You can then call customLogger('Blog saved:', `Path: ${blog.url},`, `ID: ${blog.id}`) in a route to output custom logs interleaved with the logger middleware output.
logger middleware custom PrintFunc parameter
The logger middleware accepts an optional PrintFunc function parameter with signature PrintFunc(str: string, ...rest: string[]), where str is passed by the logger and ...rest are additional string props to be printed to console.
logger middleware disable color output with NO_COLOR
Set the NO_COLOR environment variable to disable ANSI color escape codes and status code coloring. Cloudflare Workers do not have a process.env object and will default to plaintext log output.
logger middleware elapsed time format
The time taken for the request/response cycle is logged in a human-readable format, either in milliseconds (ms) or seconds (s).
logger middleware status code coloring
Response status codes are color-coded by default for better visibility and quick identification of status categories.
methodNotAllowed middleware returns 405 with Allow header
The Method Not Allowed middleware returns a 405 Method Not Allowed response with an Allow header when the request path matches a registered route but the request method is not supported. Without this middleware, Hono returns a 404 Not Found in that case.
methodNotAllowed middleware options
The methodNotAllowed middleware accepts two options: app (required, type Hono) - the Hono instance used to collect allowed methods for each path from registered routes; onMethodNotAllowed (optional, function) - generates the response with signature (c: Context, allowedMethods: string[]) => Response | Promise<Response>.
methodNotAllowed onMethodNotAllowed option
The onMethodNotAllowed option is a function with signature (c: Context, allowedMethods: string[]) => Response | Promise<Response>. It generates the response including its Allow header. By default, the middleware returns a 405 Method Not Allowed response with the Allow header set to the allowed methods.
methodNotAllowed middleware default behavior example
When a PUT request is made to /hello and only GET, HEAD, and POST are registered, methodNotAllowed returns 405 Method Not Allowed with Allow header set to 'GET, HEAD, POST'.
methodNotAllowed middleware usage
Pass the Hono app instance to methodNotAllowed with app.use(methodNotAllowed({ app })).
methodNotAllowed middleware import
Import the methodNotAllowed middleware from 'hono/method-not-allowed'.
methodOverride with custom form, header, and query options
Examples of configuring Method Override with custom field names:
```ts
app.use('/posts', methodOverride({ app, form: '_custom_name' }))
app.use('/posts', methodOverride({ app, header: 'X-METHOD-OVERRIDE' }))
app.use('/posts', methodOverride({ app, query: '_method' }))
```
methodOverride purpose
The Method Override middleware executes a handler for a different HTTP method than the actual request method, based on values from form data, headers, or query parameters. This allows HTML forms to simulate HTTP methods like DELETE that cannot be sent directly by form elements.
methodOverride middleware import
Import the Method Override middleware from 'hono/method-override'.
methodOverride options parameter reference
methodOverride accepts an options object with the following properties: app (required, type Hono) - the Hono instance used in your application; form (optional, type string, default '_method') - form key containing the method name; header (optional, type string) - header name containing the method name; query (optional, type string) - query parameter key containing the method name.
methodOverride default behavior
When methodOverride is used without form, header, or query options specified, it looks for a field named '_method' in the form data and uses its value as the HTTP method to execute.
methodOverride usage example with form
Example showing how to use Method Override middleware with default _method form field and corresponding HTML form and handler:
```ts
import { methodOverride } from 'hono/method-override'
const app = new Hono()
app.use('/posts', methodOverride({ app }))
app.delete('/posts', () => {
// ...
})
```
```html
<form action="/posts" method="POST">
<input type="hidden" name="_method" value="DELETE" />
<input type="text" name="id" />
</form>
```
Language Detector progressive locale matching
When a detected locale code like ja-JP is not in supportedLanguages, the Language Detector middleware progressively truncates subtags to find a match. For example, zh-Hant-CN will try zh-Hant, then zh. An exact match is always preferred. For example, Accept-Language: ja-JP will match 'ja', and Accept-Language: zh-Hant-CN will match 'zh-Hant'.
Language Detector path-based detection example
Example of path-based detection configuration:
app.use(
languageDetector({
order: ['path', 'cookie', 'querystring', 'header'],
lookupFromPathIndex: 0,
supportedLanguages: ['en', 'ar'],
fallbackLanguage: 'en',
})
)
This configuration prioritizes URL path detection where lookupFromPathIndex: 0 means the first path segment (e.g., /en/profile → index 0 = 'en') is used for language detection.
Language Detector basic usage example
Example of basic Language Detector usage:
const app = new Hono()
app.use(
languageDetector({
supportedLanguages: ['en', 'ar', 'ja'],
fallbackLanguage: 'en',
})
)
app.get('/', (c) => {
const lang = c.get('language')
return c.text(`Hello! Your language is ${lang}`)
})
This example configures the middleware to detect language from query string, cookie, and header in the default order, with English as the fallback language.
Language Detector options reference table
Language Detector accepts the following options:
Basic Options:
- supportedLanguages (string[], required): Allowed language codes. Default: ['en']
- fallbackLanguage (string, required): Default language. Default: 'en'
- order (DetectorType[], optional): Detection sequence. Default: ['querystring', 'cookie', 'header']
- debug (boolean, optional): Enable logging. Default: false
Detection Options:
- lookupQueryString (string, optional): Query parameter name. Default: 'lang'
- lookupCookie (string, optional): Cookie name. Default: 'language'
- lookupFromHeaderKey (string, optional): Header name. Default: 'accept-language'
- lookupFromPathIndex (number, optional): Path segment index. Default: 0
Cookie Options:
- caches (CacheType[] | false, optional): Cache settings. Default: ['cookie']
- cookieOptions.path (string, optional): Cookie path. Default: '/'
- cookieOptions.sameSite ('Strict' | 'Lax' | 'None', optional): SameSite policy. Default: 'Strict'
- cookieOptions.secure (boolean, optional): HTTPS only. Default: true
- cookieOptions.maxAge (number, optional): Expiration in seconds. Default: 31536000
- cookieOptions.httpOnly (boolean, optional): JS accessibility. Default: true
- cookieOptions.domain (string, optional): Cookie domain. Default: undefined
Advanced Options:
- ignoreCase (boolean, optional): Case-insensitive matching. Default: true
- convertDetectedLanguage ((lang: string) => string, optional): Language code transformer. Default: undefined
Language Detector default configuration
The default configuration for Language Detector is: order: ['querystring', 'cookie', 'header'], lookupQueryString: 'lang', lookupCookie: 'language', lookupFromHeaderKey: 'accept-language', lookupFromPathIndex: 0, caches: ['cookie'], ignoreCase: true, fallbackLanguage: 'en', supportedLanguages: ['en'], cookieOptions: { sameSite: 'Strict', secure: true, maxAge: 365 * 24 * 60 * 60 (31536000 seconds), httpOnly: true }, debug: false.
Language Detector default detection order
The default detection order for the Language Detector middleware is: query parameter (?lang=ar), cookie (language=ar), and Accept-Language header. The middleware checks sources in this sequence.
Language Detector detects user preferred language from multiple sources
The Language Detector middleware automatically determines a user's preferred language (locale) from various sources and makes it available via c.get('language'). Detection strategies include query parameters, cookies, headers, and URL path segments. It is designed for internationalization (i18n) and locale-specific content.
Language Detector middleware import
Import the Language Detector middleware using: import { languageDetector } from 'hono/language'
Language Detector debug logging
To enable debug logging in the Language Detector middleware, set debug: true. This will log detection steps showing which source the language was detected from (e.g., 'Detected from querystring: ar').
Language Detector validation rules
The Language Detector middleware enforces the following validation rules: fallbackLanguage must be in supportedLanguages (throws error during setup), lookupFromPathIndex must be >= 0, invalid configurations throw errors during middleware initialization, and failed detections silently use fallbackLanguage.
Language Detector client examples
The Language Detector middleware can be tested with the following client examples:
Via path: curl http://localhost:8787/ar/home
Via query parameter: curl http://localhost:8787/?lang=ar
Via cookie: curl -H 'Cookie: language=ja' http://localhost:8787/
Via header: curl -H 'Accept-Language: ar,en;q=0.9' http://localhost:8787/
Language Detector path-based routing example
Example of path-based routing with Language Detector:
app.get('/:lang/home', (c) => {
const lang = c.get('language')
return c.json({ message: getLocalizedContent(lang) })
})
This retrieves the detected language via c.get('language') to serve locale-specific content.
Language Detector multiple supported languages with normalization
Example of supporting multiple language variants with normalization:
languageDetector({
supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'],
convertDetectedLanguage: (lang) => lang.replace('_', '-'),
})
This configuration supports language variants like en-GB and ar-EG, and uses a transformer to normalize underscore-separated codes to hyphenated format.
Language Detector cookie configuration example
Example of custom cookie configuration:
app.use(
languageDetector({
lookupCookie: 'app_lang',
caches: ['cookie'],
cookieOptions: {
path: '/',
sameSite: 'Lax',
secure: true,
maxAge: 86400 * 365,
httpOnly: true,
domain: '.example.com',
},
})
)
This configures a custom cookie name 'app_lang' and custom cookie options including path, sameSite policy, secure flag, expiration, httpOnly flag, and domain.
Language Detector language code transformation example
Example of normalizing complex language codes:
app.use(
languageDetector({
convertDetectedLanguage: (lang) => lang.split('-')[0],
supportedLanguages: ['en', 'ja'],
fallbackLanguage: 'en',
})
)
This example uses a transformer function to normalize language codes by extracting only the primary language code (e.g., en-US → en).
Language Detector disable cookie caching
To disable cookie caching in the Language Detector middleware, set caches: false in the configuration: languageDetector({ caches: false })
prettyJSON middleware options
The prettyJSON middleware accepts three optional options: space (number, default 2) - the number of spaces for indentation; query (string, default 'pretty') - the name of the query string parameter for applying pretty printing; force (boolean, default false) - when true, JSON responses are always prettified regardless of the query parameter.
Pretty JSON middleware overview
Pretty JSON middleware enables JSON pretty print for JSON response bodies. When `?pretty` is added to the URL query parameter, JSON strings are prettified with proper indentation and line breaks.
Import prettyJSON middleware
Import prettyJSON from 'hono/pretty-json': import { prettyJSON } from 'hono/pretty-json'
prettyJSON middleware usage example
The prettyJSON middleware is used with app.use(prettyJSON()) and can accept options. Example: const app = new Hono(); app.use(prettyJSON({ space: 4 })); app.get('/', (c) => { return c.json({ message: 'Hono!' }) })
Request ID Middleware options
The requestId middleware accepts three optional options: limitLength (number, default 255) sets the maximum length of the request ID; headerName (string, default 'X-Request-Id') sets the header name for the request ID; generator ((c: Context) => string) provides a custom request ID generation function, defaulting to crypto.randomUUID().
Request ID Middleware custom header behavior
If a custom request ID is provided in the specified header (default 'X-Request-Id'), the middleware will use that value instead of generating a new one. Set headerName to an empty string to disable reading the request ID from headers.