> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/honojs/hono/llms.txt
> Use this file to discover all available pages before exploring further.

# csrf

> API reference for the CSRF Protection middleware

## Import

```typescript theme={null}
import { csrf } from 'hono/csrf'
```

## Usage

```typescript theme={null}
const app = new Hono()

// Default: both origin and sec-fetch-site validation
app.use('*', csrf())

app.post('/protected', (c) => {
  return c.text('Protected action')
})
```

## Options

The `csrf` middleware accepts an optional `CSRFOptions` object:

<ParamField path="origin" type="string | string[] | ((origin: string, context: Context) => boolean | Promise<boolean>)">
  Allowed origins for requests. Can be:

  * A single origin string (e.g., `'https://example.com'`)
  * An array of allowed origins
  * A function that returns `true` to allow or `false` to deny
  * Default: Only same origin as the request URL
</ParamField>

<ParamField path="secFetchSite" type="'same-origin' | 'same-site' | 'cross-site' | 'none' | string[] | ((secFetchSite: string, context: Context) => boolean | Promise<boolean>)">
  Sec-Fetch-Site header validation. Can be:

  * A single allowed value (e.g., `'same-origin'`)
  * An array of allowed values (e.g., `['same-origin', 'same-site']`)
  * A function that returns `true` to allow or `false` to deny
  * Default: Only allows `'same-origin'`
</ParamField>

## Signature

```typescript theme={null}
csrf(options?: CSRFOptions): MiddlewareHandler
```

## Examples

### Basic usage

```typescript theme={null}
const app = new Hono()

app.use('*', csrf())
```

### Allow specific origins

```typescript theme={null}
app.use('*', csrf({ origin: 'https://example.com' }))
```

### Multiple origins

```typescript theme={null}
app.use('*', csrf({ 
  origin: ['https://app.com', 'https://api.com'] 
}))
```

### Allow same-site requests

```typescript theme={null}
app.use('*', csrf({ 
  secFetchSite: 'same-origin' 
}))
```

### Allow multiple Sec-Fetch-Site values

```typescript theme={null}
app.use('*', csrf({ 
  secFetchSite: ['same-origin', 'same-site'] 
}))
```

### Dynamic Sec-Fetch-Site validation

```typescript theme={null}
app.use('*', csrf({
  secFetchSite: (secFetchSite, c) => {
    // Always allow same-origin
    if (secFetchSite === 'same-origin') return true
    
    // Allow cross-site for webhook endpoints
    if (secFetchSite === 'cross-site' && c.req.path.startsWith('/webhook/')) {
      return true
    }
    
    return false
  }
}))
```

### Dynamic origin validation

```typescript theme={null}
app.use('*', csrf({
  origin: (origin, c) => {
    // Allow same origin
    if (origin === new URL(c.req.url).origin) return true
    
    // Allow specific trusted domains
    return ['https://app.example.com', 'https://admin.example.com'].includes(origin)
  }
}))
```

### Async validation

```typescript theme={null}
app.use('*', csrf({
  origin: async (origin, c) => {
    // Check against database
    const allowed = await db.trustedOrigins.exists(origin)
    return allowed
  }
}))
```

## Behavior

* Only validates non-safe HTTP methods (POST, PUT, DELETE, PATCH, etc.)
* Only validates form submissions (Content-Type: `application/x-www-form-urlencoded`, `multipart/form-data`, or `text/plain`)
* Request is allowed if EITHER validation passes:
  * `Sec-Fetch-Site` header matches allowed values, OR
  * `Origin` header matches allowed origins
* Returns 403 Forbidden if both validations fail
* Safe methods (GET, HEAD) are always allowed
* If neither `Origin` nor `Sec-Fetch-Site` header is present, request is denied
