honojs/hono · error · HTTPException
Forbidden
Error message
Forbidden
What it means
Hono's ipRestriction middleware throws a 403 Forbidden error (via blockError) when it cannot determine the client's IP address. The middleware reads connection info (getIP(c)) and if no remote address is resolvable, it fails closed and blocks the request rather than allowing an unidentified client through.
Source
Thrown at src/middleware/ip-restriction/index.ts:242
) => Response | Promise<Response>
): MiddlewareHandler => {
const allowLength = allowList.length
const denyMatcher = buildMatcher(denyList)
const allowMatcher = buildMatcher(allowList)
const blockError = (c: Context): HTTPException =>
new HTTPException(403, {
res: c.text('Forbidden', {
status: 403,
}),
})
return async function ipRestriction(c, next) {
const connInfo = getIP(c)
const addr = typeof connInfo === 'string' ? connInfo : connInfo.remote.address
if (!addr) {
throw blockError(c)
}
const type =
(typeof connInfo !== 'string' && connInfo.remote.addressType) || distinctRemoteAddr(addr)
const remoteData = { addr, type, isIPv4: type === 'IPv4' }
try {
if (denyMatcher(remoteData)) {
if (onError) {
return onError({ addr, type }, c)
}
throw blockError(c)
}
if (allowMatcher(remoteData)) {
return await next()
}
} catch (e) {
if (View on GitHub (pinned to e2740d5a1b)
Solutions
- Ensure the runtime adapter provides connection info (e.g. serve({ fetch: app.fetch, port }, (info) => ({ remote: info })) for @hono/node-server / Bun)
- Test IP restriction with a real HTTP request instead of app.request()
- If behind a trusted proxy, configure getIP to read a forwarded header like x-forwarded-for so an address is always resolvable
Example fix
// before
const app = new Hono()
app.use(ipRestriction(getIPs /* no conninfo configured */))
// after (Node.js)
import { serve } from '@hono/node-server'
serve({ fetch: app.fetch, port: 3000 }, (info) => ({ remote: info })) Defensive patterns
Strategy: validation
Validate before calling
const getConnIP = (c: Context): string | undefined => {
try {
const info = c.env?.conninfo ?? getIP(c)
return typeof info === 'string' ? info : info?.remote?.address
} catch {
return undefined
}
} Try / catch
app.onError((err, c) => {
if (err instanceof HTTPException && err.status === 403) {
return c.text('Access denied', 403)
}
throw err
}) Prevention
- Configure the server adapter to supply conninfo (serve options callback)
- Test IP middleware with real HTTP requests, not app.request()
- Behind a proxy, derive the IP from x-forwarded-for in getIP
When it happens
Trigger: Using secureHeaders/ipRestriction middleware where connInfo is undefined or connInfo.remote.address is missing — e.g. running under a runtime or adapter that doesn't provide Hono's conninfo helper (plain Node server without getConnectionInfo, Bun, some edge runtimes), or testing with app.request() where no socket exists.
Common situations: Adding IP restrictions in local dev where req.raw has no connection info; deploying behind a proxy that strips connection data; forgetting to wire an app.getConnectionHelper or use the correct runtime adapter that populates conninfo.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Context is not finalized. Did you forget to return a Respons
- basic auth middleware requires options for "username and pas
- bearer auth middleware requires options for "token" or "veri
- Invalid rule: ${rule}
- env has to include the 2nd argument of fetch.
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/2e24c7c60fa4a6ce.
Report an issue: GitHub.