> ## 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.

# etag

> API reference for the ETag middleware

## Import

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

## Usage

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

app.use('/etag/*', etag())

app.get('/etag/abc', (c) => {
  return c.text('Hono is hot')
})
```

## Options

The `etag` middleware accepts an optional `ETagOptions` object:

<ParamField path="weak" type="boolean" default={false}>
  Define using weak validation. If `true`, then `W/` is added to the prefix of the ETag value.
</ParamField>

<ParamField path="retainedHeaders" type="string[]" default={['cache-control', 'content-location', 'date', 'etag', 'expires', 'vary']}>
  The headers that you want to retain in the 304 Not Modified response. By default, includes headers required by the HTTP spec for 304 responses.
</ParamField>

<ParamField path="generateDigest" type="(body: Uint8Array) => ArrayBuffer | Promise<ArrayBuffer>">
  A custom digest generation function. By default, it uses SHA-1 algorithm via `crypto.subtle.digest`. This function is called with the response body as a Uint8Array and should return a hash as an ArrayBuffer or a Promise of one.
</ParamField>

## Signature

```typescript theme={null}
etag(options?: ETagOptions): MiddlewareHandler
```

## Constants

### RETAINED\_304\_HEADERS

```typescript theme={null}
const RETAINED_304_HEADERS = [
  'cache-control',
  'content-location',
  'date',
  'etag',
  'expires',
  'vary',
]
```

Default headers to pass through on 304 responses as specified by HTTP spec.

## Examples

### Basic usage

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

app.use('/etag/*', etag())

app.get('/etag/abc', (c) => {
  return c.text('Hono is hot')
})
```

### Weak validation

```typescript theme={null}
app.use('/api/*', etag({ weak: true }))

app.get('/api/data', (c) => {
  return c.json({ data: 'example' })
})

// Response: ETag: W/"abc123"
```

### Custom retained headers

```typescript theme={null}
app.use('/api/*', etag({
  retainedHeaders: ['etag', 'cache-control', 'date', 'x-custom-header']
}))
```

### Custom digest function

```typescript theme={null}
import { createHash } from 'crypto'

app.use('/api/*', etag({
  generateDigest: async (body) => {
    // Use MD5 instead of SHA-1
    const hash = createHash('md5')
    hash.update(body)
    return hash.digest().buffer
  }
}))
```

### Apply to specific routes

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

app.use('/static/*', etag())

app.get('/static/file', (c) => {
  return c.text('Static content')
})
```

## Behavior

* Generates ETag header from response body using SHA-1 hash (by default)
* If response already has an ETag header, uses it instead of generating a new one
* Compares ETag with `If-None-Match` header from request
* Returns 304 Not Modified if ETags match
* 304 responses only include retained headers (specified by spec or config)
* Weak validation uses `W/` prefix for ETags
* If digest cannot be generated (e.g., no body), skips ETag generation
* If `crypto.subtle` is not available and no custom `generateDigest` is provided, skips ETag generation
