---
title: "CSRF and Body Limit"
description: "Hono provides built-in middleware to protect web applications against Cross-Site Request Forgery (CSRF) and control inbound request payload sizesCSRF Protection Middleware for Hono, Body Limit Midd..."
last_updated: "2026-10-12T08:14:12.743647+00:00"
canonical_url: "https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/security-middleware/csrf-and-body-limit"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [src/middleware/body-limit/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts)
- [src/middleware/secure-headers/secure-headers.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/secure-headers/secure-headers.ts)
- [src/middleware/csrf/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts)
- [src/middleware/ip-restriction/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/ip-restriction/index.ts)
- [src/adapter/aws-lambda/handler.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/aws-lambda/handler.ts)
- [src/middleware/method-override/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/method-override/index.ts)
- [src/request.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/request.ts)
- [src/utils/body.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/body.ts)
- [src/middleware/jwk/jwk.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/jwk/jwk.ts)
- [src/middleware/jwt/jwt.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/jwt/jwt.ts)
- [src/validator/validator.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/validator/validator.ts)
- [src/middleware/cors/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/cors/index.ts)
- [src/middleware/etag/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/etag/index.ts)
- [src/helper/streaming/text.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/text.ts)
- [src/middleware/bearer-auth/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/bearer-auth/index.ts)
- [src/adapter/lambda-edge/handler.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/lambda-edge/handler.ts)
- [src/middleware/compress/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/compress/index.ts)
- [src/middleware/language/language.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/language/language.ts)
- [src/utils/buffer.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/buffer.ts)
- [src/middleware/etag/digest.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/etag/digest.ts)
- [src/client/fetch-result-please.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/client/fetch-result-please.ts)
- [src/context.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/context.ts)
- [src/adapter/vercel/conninfo.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/vercel/conninfo.ts)
- [src/adapter/lambda-edge/conninfo.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/lambda-edge/conninfo.ts)
- [src/helper/streaming/utils.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/utils.ts)
- [src/middleware/secure-headers/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/secure-headers/index.ts)
- [src/utils/compress.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/compress.ts)
- [src/middleware/request-id/request-id.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/request-id/request-id.ts)
- [src/middleware/secure-headers/permissions-policy.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/secure-headers/permissions-policy.ts)
- [src/middleware/jwk/keys.test.json](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/jwk/keys.test.json)
</details>

## Overview

Hono provides built-in middleware to protect web applications against Cross-Site Request Forgery (CSRF) and control inbound request payload sizes[CSRF Protection Middleware for Hono](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L1-L3), [Body Limit Middleware for Hono](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts#L1-L4). The CSRF middleware evaluates request security by validating `Origin` headers and `Sec-Fetch-Site` values against safe and mutating HTTP method checks[CSRF Protection Middleware for Hono](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L32-L36). Concurrently, the body limit middleware safeguards server resources by inspecting `Content-Length` headers or incrementally tracking streaming request bodies against configured maximum size limits[Body Limit Middleware for Hono](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts#L18-L25), [src/middleware/body-limit/index.ts:61-94](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts#L61-L94). Together, these components enforce robust request boundaries across diverse edge and serverless environments. Sources: [CSRF Protection Middleware for Hono](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L1-L3), [Body Limit Middleware for Hono](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts#L1-L4), [src/middleware/csrf/index.ts:32-36](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L32-L36), [src/middleware/body-limit/index.ts:18-25](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts#L18-L25), [src/middleware/body-limit/index.ts:61-94](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts#L61-L94)

## Cross-Site Request Forgery Protection Architecture

### Public API Surface and Method Checks

Hono exports the `csrf()` factory function which returns a standard `MiddlewareHandler`[src/middleware/csrf/index.ts:94-150](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L94-L150). The middleware evaluates incoming requests against safe and mutating HTTP method classifications[src/middleware/csrf/index.ts:139-144](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L139-L144). Safe methods are matched using the regular expression `/^(GET|HEAD)$/`, exempting read-only requests from validation[src/middleware/csrf/index.ts:28|28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L28-L28), [src/middleware/csrf/index.ts:139-140](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L139-L140). Mutating requests undergo inspection only if their `content-type` header matches form element submission patterns defined by `/^\b(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)\b/i`[src/middleware/csrf/index.ts:29-30](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L29-L30), [src/middleware/csrf/index.ts:140-141](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L140-L141). If both `sec-fetch-site` and `origin` validations fail on a matching mutating request, the middleware instantiates a `Response` with body `'Forbidden'` and status `403`, throwing an `HTTPException(403)`[src/middleware/csrf/index.ts:142-147](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L142-L147). Otherwise, control advances via `await next()`[src/middleware/csrf/index.ts:149-149](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L149-L149).

```typescript
const isSafeMethodRe = /^(GET|HEAD)$/
const isRequestedByFormElementRe =
  /^\b(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)\b/i
```
Sources: [src/middleware/csrf/index.ts:28-30](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L28-L30), [src/middleware/csrf/index.ts:94-150](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L94-L150)

```typescript
return async function csrf(c, next) {
  if (
    !isSafeMethodRe.test(c.req.method) &&
    isRequestedByFormElementRe.test(c.req.header('content-type') || 'text/plain') &&
    !(await isAllowedSecFetchSite(c.req.header('sec-fetch-site'), c)) &&
    !(await isAllowedOrigin(c.req.header('origin'), c))
  ) {
    const res = new Response('Forbidden', { status: 403 })
    throw new HTTPException(403, { res })
  }

  await next()
}
```
Sources: [src/middleware/csrf/index.ts:138-150](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L138-L150)

> [!WARNING]
> Requests lacking both `sec-fetch-site` and `origin` headers during mutating form submissions will fail validation because both `isAllowedSecFetchSite` and `isAllowedOrigin` return `false` when their respective headers are undefined.
> Sources: [src/middleware/csrf/index.ts:106-112](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L106-L112), [src/middleware/csrf/index.ts:126-136](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L126-L136)

### Verification Call Chain

The execution trace for Sec-Fetch-Site validation flows through distinct functions defined across the module. 

1. `csrf` — Intercepts the request inside the returned middleware handler and invokes `isAllowedSecFetchSite` with the `sec-fetch-site` header[src/middleware/csrf/index.ts:138-142](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L138-L142).
2. `isAllowedSecFetchSite` — Checks whether the header value is defined and passes the type guard `isSecFetchSite` before calling the configured `secFetchSiteHandler`[src/middleware/csrf/index.ts:126-136](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L126-L136).
3. `isSecFetchSite` — Validates that the string matches one of the permitted union constants[src/middleware/csrf/index.ts:15-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L15-L16).

```mermaid
sequenceDiagram
    participant CSRF as csrf
    participant IASFS as isAllowedSecFetchSite
    participant ISFS as isSecFetchSite

    CSRF->>IASFS: c.req.header('sec-fetch-site')
    IASFS->>ISFS: secFetchSite string
    ISFS-->>IASFS: boolean type guard result
    IASFS-->>CSRF: boolean validation outcome
```

Sources: [src/middleware/csrf/index.ts:15-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L15-L16), [src/middleware/csrf/index.ts:126-136](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L126-L136), [src/middleware/csrf/index.ts:138-142](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L138-L142)

### Permitted Sec-Fetch-Site Constants

The module defines explicit runtime values and type constraints for browser-supplied `Sec-Fetch-Site` headers.

| Constant Array / Type | Value | Meaning / Role |
| :--- | :--- | :--- |
| `secFetchSiteValues` | `'same-origin'` | Request originated from the same site and origin. |
| `secFetchSiteValues` | `'same-site'` | Request originated from a different path on the same site. |
| `secFetchSiteValues` | `'none'` | Request was triggered by a direct user interaction like bookmark navigation. |
| `secFetchSiteValues` | `'cross-site'` | Request originated from a completely external domain. |

```typescript
const secFetchSiteValues = ['same-origin', 'same-site', 'none', 'cross-site'] as const
type SecFetchSite = (typeof secFetchSiteValues)[number]

const isSecFetchSite = (value: string): value is SecFetchSite =>
  (secFetchSiteValues as readonly string[]).includes(value)
```
Sources: [src/middleware/csrf/index.ts:12-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L12-L16)

## CSRF Origin Resolution and Custom Matchers

### Origin and Sec-Fetch-Site Matcher Resolution

The CSRF middleware normalizes both `origin` and `secFetchSite` options during initialization through higher-order function factories. When `options.origin` is omitted, the default handler extracts the origin from the request URL using `new URL(c.req.url).origin`. If `options.origin` is provided as a string, it constructs an exact equality matcher. If it is an array, it evaluates membership using `optsOrigin.includes(origin)`. When supplied as a custom function, the middleware delegates directly to the user-defined handler callback.

```typescript
const originHandler: IsAllowedOriginHandler = ((optsOrigin) => {
  if (!optsOrigin) {
    return (origin, c) => origin === new URL(c.req.url).origin
  } else if (typeof optsOrigin === 'string') {
    return (origin) => origin === optsOrigin
  } else if (typeof optsOrigin === 'function') {
    return optsOrigin
  } else {
    return (origin) => optsOrigin.includes(origin)
  }
})(options?.origin)
```
Sources: [src/middleware/csrf/index.ts:95-105](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L95-L105)

Similarly, `secFetchSiteHandler` builds its validation logic based on the input type of `options.secFetchSite`. If undefined, it restricts permitted traffic exclusively to `'same-origin'`. String, array, and function options follow identical resolution branches to evaluate browser metadata against configured rules.

```typescript
const secFetchSiteHandler: IsAllowedSecFetchSiteHandler = ((optsSecFetchSite) => {
  if (!optsSecFetchSite) {
    // Default: only allow same-origin
    return (secFetchSite) => secFetchSite === 'same-origin'
  } else if (typeof optsSecFetchSite === 'string') {
    return (secFetchSite) => secFetchSite === optsSecFetchSite
  } else if (typeof optsSecFetchSite === 'function') {
    return optsSecFetchSite
  } else {
    return (secFetchSite) => optsSecFetchSite.includes(secFetchSite)
  }
})(options?.secFetchSite)
```
Sources: [src/middleware/csrf/index.ts:114-125](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L114-L125)

> [!NOTE]
> If the `origin` header is entirely absent from the incoming request, `isAllowedOrigin` immediately short-circuits and returns `false` without invoking the underlying handler.
> Sources: [src/middleware/csrf/index.ts:106-112](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L106-L112)

### Custom Matcher Signatures and Handlers

Both `origin` and `secFetchSite` support asynchronous or synchronous custom handler functions that receive the header value alongside the active [execution Context](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/execution-context). These hooks allow developers to implement granular policies such as validating specific subdomain patterns or permitting cross-site requests exclusively for designated webhook endpoints.

```typescript
export type IsAllowedOriginHandler = (origin: string, context: Context) => boolean | Promise<boolean>

export type IsAllowedSecFetchSiteHandler = (
  secFetchSite: SecFetchSite,
  context: Context
) => boolean | Promise<boolean>
```

```typescript
type IsAllowedOriginHandler = (origin: string, context: Context) => boolean | Promise<boolean>

const secFetchSiteValues = ['same-origin', 'same-site', 'none', 'cross-site'] as const
type SecFetchSite = (typeof secFetchSiteValues)[number]

const isSecFetchSite = (value: string): value is SecFetchSite =>
  (secFetchSiteValues as readonly string[]).includes(value)

type IsAllowedSecFetchSiteHandler = (
  secFetchSite: SecFetchSite,
  context: Context
) => boolean | Promise<boolean>
```
Sources: [src/middleware/csrf/index.ts:10-21](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L10-L21)

> [!WARNING]
> When `sec-fetch-site` contains an unrecognised string value not present in the runtime `secFetchSiteValues` tuple, `isSecFetchSite` evaluates to `false` and causes validation to fail before the custom `secFetchSiteHandler` can execute.
> Sources: [src/middleware/csrf/index.ts:12-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L12-L16), [src/middleware/csrf/index.ts:131-135](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts#L131-L135)

## Body Limit Middleware and Stream Wrapping

### Public API Surface and Configuration Options

The body limit middleware restricts incoming request payload sizes to protect applications against denial-of-service attacks [Execution Context](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/execution-context) caused by oversized payloads. It exports a `bodyLimit` factory function that accepts a `BodyLimitOptions` configuration object specifying the `maxSize` threshold in bytes and an optional custom error handler function `onError`.

| Option Property | Type | Default Behavior | Purpose |
| :--- | :--- | :--- | :--- |
| `maxSize` | `number` | *Required* | Maximum allowable body size in bytes. |
| `onError` | `OnError` | Throws `HTTPException(413)` with text `'Payload Too Large'` | Handler invoked when `maxSize` is exceeded. |

```typescript
type OnError = (c: Context) => Response | Promise<Response>
type BodyLimitOptions = {
  maxSize: number
  onError?: OnError
}
```
Sources: [src/middleware/body-limit/index.ts:12-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:12-16)

```typescript
export const bodyLimit = (options: BodyLimitOptions): MiddlewareHandler => {
  const onError: OnError =
    options.onError ||
    (() => {
      const res = new Response(ERROR_MESSAGE, {
        status: 413,
      })
      throw new HTTPException(413, { res })
    })
  const maxSize = options.maxSize
```
Sources: [src/middleware/body-limit/index.ts:50-59](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:50-59)

> [!NOTE]
> If `options.onError` is omitted, the default error handler constructs a standard Web `Response` with status code `413` and throws an `HTTPException`.
> Sources: [src/middleware/body-limit/index.ts:51-58](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:51-58)

### Content-Length Pre-Checks and Chunk Tracking

When an incoming request reaches the middleware, it first evaluates whether a request body exists. If `c.req.raw.body` is null or undefined (such as standard `GET` or `HEAD` requests), the middleware bypasses inspection and invokes `next()` immediately.

```typescript
return async function bodyLimit(c, next) {
  if (!c.req.raw.body) {
    // maybe GET or HEAD request
    return next()
  }
```
Sources: [src/middleware/body-limit/index.ts:61-65](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:61-65)

If headers contain a `content-length` without a `transfer-encoding` header, the middleware performs a fast pre-check by parsing the length directly. If `content-length` exceeds `maxSize`, it immediately invokes `onError(c)` without consuming the stream.

```typescript
const hasTransferEncoding = c.req.raw.headers.has('transfer-encoding')
const hasContentLength = c.req.raw.headers.has('content-length')

if (hasContentLength && !hasTransferEncoding) {
  // Only Content-Length present - we can trust it
  const contentLength = parseInt(c.req.raw.headers.get('content-length') || '0', 10)
  return contentLength > maxSize ? onError(c) : next()
}
```
Sources: [src/middleware/body-limit/index.ts:67-74](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:67-74)

When `transfer-encoding` (chunked) is present or length headers are missing entirely, per RFC 7230, transfer-encoding takes precedence. The middleware consumes the underlying `ReadableStream` chunk by chunk using a `ReadableStreamDefaultReader`, accumulating byte lengths into a running `size` counter and pushing each `Uint8Array` chunk into an array. If accumulated size exceeds `maxSize` at any point during iteration, processing terminates instantly and invokes `onError(c)`.

```typescript
// Transfer-Encoding present (chunked) or no length headers.
// Per RFC 7230, when both are present Transfer-Encoding takes precedence
// and Content-Length is ignored. Read the body up-front so the size check
// is final before the handler runs, regardless of how (or whether) the
// handler reads the body.
let size = 0
const chunks: Uint8Array[] = []
const rawReader = c.req.raw.body.getReader()
for (;;) {
  const { done, value } = await rawReader.read()
  if (done) {
    break
  }
  size += value.length
  if (size > maxSize) {
    return onError(c)
  }
  chunks.push(value)
}
```
Sources: [src/middleware/body-limit/index.ts:76-94](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:76-94)

> [!WARNING]
> Because chunked or length-less streams are read entirely into memory arrays during validation, extremely large streaming payloads will consume memory proportional to their size up to the threshold before being rejected or forwarded.
> Sources: [src/middleware/body-limit/index.ts:81-94](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:81-94)

### Streaming Body Reconstruction and Replacement

Once a chunked or un-sized stream successfully passes the byte limit verification, the accumulated chunks must be restored so downstream route handlers and parsers can read the request body normally. The middleware constructs a fresh `ReadableStream` that enqueues the saved `Uint8Array` chunks in sequence and closes the controller.

```typescript
const requestInit: RequestInit & { duplex: 'half' } = {
  body: new ReadableStream({
    start(controller) {
      for (const chunk of chunks) {
        controller.enqueue(chunk)
      }
      controller.close()
    },
  }),
```
Sources: [src/middleware/body-limit/index.ts:96-104](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:96-104)

It then instantiates a new standard `Request` object wrapping the fresh readable stream with explicit `duplex: 'half'` options, assigns it back to `c.req.raw`, and proceeds down the middleware chain via `next()`.

```typescript
  duplex: 'half',
}
c.req.raw = new Request(c.req.raw, requestInit as RequestInit)

```
Sources: [src/middleware/body-limit/index.ts:105-108](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:105-108)

## Payload Overflow Detection and Error Handling

### Error Trigger Conditions and HTTPException 413

When the body limit middleware detects that a payload exceeds the configured `maxSize`, it immediately halts further stream consumption or execution and invokes the error handler. For requests governed by a trustworthy `content-length` header, this triggers as soon as `content-length > maxSize`. For chunked or un-sized streams, the check occurs incrementally during stream iteration inside the reader loop whenever the running `size` counter surpasses `maxSize`.

```typescript
if (hasContentLength && !hasTransferEncoding) {
  // Only Content-Length present - we can trust it
  const contentLength = parseInt(c.req.raw.headers.get('content-length') || '0', 10)
  return contentLength > maxSize ? onError(c) : next()
}
```
Sources: [src/middleware/body-limit/index.ts:70-74](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:70-74)

```typescript
  size += value.length
  if (size > maxSize) {
    return onError(c)
  }
  chunks.push(value)
}
```
Sources: [src/middleware/body-limit/index.ts:89-94](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:89-94)

> [!CAUTION]
> When consuming chunked streams, the reader halts the moment `size > maxSize` is met mid-stream, preventing excess bytes from accumulating into memory while terminating the active request stream early.
> Sources: [src/middleware/body-limit/index.ts:89-94](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:89-94)

### Custom onError Callbacks and Default Response Termination

Developers can supply a custom `onError` callback function within `BodyLimitOptions` to customize how payload overflows are formatted and returned to clients. If `options.onError` is omitted, the middleware falls back to a default handler that instantiates a web standard `Response` using the constant `ERROR_MESSAGE` string (`'Payload Too Large'`) with status code `413`, and immediately throws an `HTTPException(413, { res })`.

```typescript
const ERROR_MESSAGE = 'Payload Too Large'

type OnError = (c: Context) => Response | Promise<Response>
type BodyLimitOptions = {
  maxSize: number
  onError?: OnError
}
```
Sources: [src/middleware/body-limit/index.ts:10-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:10-16)

```typescript
export const bodyLimit = (options: BodyLimitOptions): MiddlewareHandler => {
  const onError: OnError =
    options.onError ||
    (() => {
      const res = new Response(ERROR_MESSAGE, {
        status: 413,
      })
      throw new HTTPException(413, { res })
    })
```
Sources: [src/middleware/body-limit/index.ts:50-58](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:50-58)

```typescript
  return contentLength > maxSize ? onError(c) : next()
}
```
Sources: [src/middleware/body-limit/index.ts:73-74](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:73-74)

```typescript
if (size > maxSize) {
  return onError(c)
}
```
Sources: [src/middleware/body-limit/index.ts:90-92](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:90-92)

> [!NOTE]
> The `OnError` type signature specifies that custom handlers accept the active `Context` (`c`) and return either a `Response` or a `Promise<Response>`, allowing seamless integration with context response helpers like `c.text()`.
> Sources: [src/middleware/body-limit/index.ts:12-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:12-16)

## Security Configuration and Defense in Depth

### Proxy Adapters and Edge Request Initialization

When deploying security middleware like CSRF protection and body limits behind serverless runtimes or edge proxies, incoming event structures must be accurately transformed into web standard `Request` instances. Adapters such as `aws-lambda` and `lambda-edge` process incoming events by parsing HTTP headers, methods, query strings, and body payloads before dispatching them into Hono's `app.fetch()`.

```typescript
const createRequest = (event: CloudFrontEdgeEvent): Request => {
  const queryString = event.Records[0].cf.request.querystring
  const host =
    event.Records[0].cf.request.headers?.host?.[0]?.value ||
    event.Records[0].cf.config.distributionDomainName
  const urlPath = `https://${host}${event.Records[0].cf.request.uri}`
  const url = queryString ? `${urlPath}?${queryString}` : urlPath

  const headers = new Headers()
  Object.entries(event.Records[0].cf.request.headers).forEach(([k, v]) => {
    v.forEach((header) => headers.append(k, header.value))
  })

  const requestBody = event.Records[0].cf.request.body
  const method = event.Records[0].cf.request.method
  const rawBody = createBody(method, requestBody)

  let body: string | Uint8Array<ArrayBuffer> | undefined = rawBody
  if (rawBody !== undefined) {
    const bytes =
      typeof rawBody === 'string'
        ? (new TextEncoder().encode(rawBody) as Uint8Array<ArrayBuffer>)
        : rawBody
    body = bytes
    headers.set('content-length', bytes.length.toString())
  }

  return new Request(url, {
    headers,
    method,
    body,
  })
}
```
Sources: [src/adapter/lambda-edge/handler.ts:152-184](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/lambda-edge/handler.ts:152-184)

> [!IMPORTANT]
> Edge adapters like Lambda@Edge manually assign content-length headers when request body bytes are present, ensuring downstream size validation and body limit middleware can inspect exact payload dimensions before handlers execute.
> Sources: [src/adapter/lambda-edge/handler.ts:169-177](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/lambda-edge/handler.ts:169-177)

### Combining CSRF Protection and Body Constraints

Layering both `csrf()` and `bodyLimit()` creates a multi-layered defense against malicious web requests. The CSRF middleware evaluates mutating HTTP methods against form element content-types (`application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain`), requiring valid `sec-fetch-site` or `origin` validation headers before allowing execution to proceed.

```typescript
return async function csrf(c, next) {
  if (
    !isSafeMethodRe.test(c.req.method) &&
    isRequestedByFormElementRe.test(c.req.header('content-type') || 'text/plain') &&
    !(await isAllowedSecFetchSite(c.req.header('sec-fetch-site'), c)) &&
    !(await isAllowedOrigin(c.req.header('origin'), c))
  ) {
    const res = new Response('Forbidden', { status: 403 })
    throw new HTTPException(403, { res })
  }

  await next()
}
```
Sources: [src/middleware/csrf/index.ts:138-150](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/csrf/index.ts:138-150)

Once origin and site context validation pass, the body limit middleware intercepts incoming streams, enforcing strict `maxSize` thresholds to reject oversized payloads with an `HTTPException(413)` before memory exhaustion occurs.

```typescript
return async function bodyLimit(c, next) {
  if (!c.req.raw.body) {
    // maybe GET or HEAD request
    return next()
  }

  const hasTransferEncoding = c.req.raw.headers.has('transfer-encoding')
  const hasContentLength = c.req.raw.headers.has('content-length')

  if (hasContentLength && !hasTransferEncoding) {
    // Only Content-Length present - we can trust it
    const contentLength = parseInt(c.req.raw.headers.get('content-length') || '0', 10)
    return contentLength > maxSize ? onError(c) : next()
  }
```
Sources: [src/middleware/body-limit/index.ts:61-74](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/body-limit/index.ts:61-74)

## Related

- [Secure Headers and CORS](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/security-middleware/secure-headers-and-cors)
- [Basic and Bearer Auth](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/security-middleware/basic-and-bearer-auth)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/llms.txt) for all pages in this wiki.
