---
title: "Static File Serving"
description: "Hono provides a unified static file serving middleware that bridges web standards with runtime-specific file system and asset APIs across different JavaScript runtimes. By abstracting lower-level f..."
last_updated: "2026-10-12T08:14:12.75099+00:00"
canonical_url: "https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/helpers-and-tooling/static-file-serving"
---

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

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

- [src/middleware/serve-static/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts)
- [src/adapter/bun/serve-static.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/bun/serve-static.ts)
- [src/middleware/secure-headers/secure-headers.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/secure-headers/secure-headers.ts)
- [src/adapter/deno/serve-static.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/deno/serve-static.ts)
- [src/helper/streaming/sse.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/sse.ts)
- [src/adapter/cloudflare-workers/serve-static-module.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-workers/serve-static-module.ts)
- [src/helper/ssg/ssg.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/ssg/ssg.ts)
- [src/middleware/cache/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/cache/index.ts)
- [src/helper/streaming/stream.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/streaming/stream.ts)
- [src/adapter/cloudflare-pages/handler.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-pages/handler.ts)
- [src/adapter/cloudflare-workers/serve-static.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-workers/serve-static.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/ip-restriction/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/ip-restriction/index.ts)
- [src/context.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/context.ts)
- [src/middleware/serve-static/path.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/path.ts)
- [src/adapter/lambda-edge/handler.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/lambda-edge/handler.ts)
- [src/helper/ssg/middleware.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/ssg/middleware.ts)
- [src/middleware/language/language.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/language/language.ts)
- [src/adapter/cloudflare-workers/utils.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-workers/utils.ts)
- [src/hono-base.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts)
- [src/adapter/cloudflare-workers/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-workers/index.ts)
- [src/adapter/deno/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/deno/index.ts)
- [src/router/reg-exp-router/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts)
- [src/adapter/cloudflare-pages/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-pages/index.ts)
- [src/utils/mime.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/mime.ts)
- [src/middleware/etag/digest.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/etag/digest.ts)
- [src/adapter/netlify/handler.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/netlify/handler.ts)
- [src/router/linear-router/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/linear-router/router.ts)
- [package.json](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/package.json)
</details>

## Overview

Hono provides a unified static file serving middleware that bridges web standards with runtime-specific file system and asset APIs across different JavaScript runtimes. By abstracting lower-level file retrieval functions, the middleware processes incoming requests to safely deliver assets, handle precompressed encodings, and integrate with environment adapters such as [Bun Adapter](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/runtime-adapters/bun-adapter) and [Deno Adapter](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/runtime-adapters/deno-adapter).

Sources: [src/middleware/serve-static/index.ts:32-49](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L32-L49), [src/adapter/bun/serve-static.ts:8-31](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/bun/serve-static.ts#L8-L31), [src/adapter/deno/serve-static.ts:8-41](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/deno/serve-static.ts#L8-L41)

## Middleware Architecture and Core Interfaces

### Public Options and Runtime Abstractions

The `ServeStaticOptions` interface defines the configuration properties accepted by the static file serving middleware. It supports setting a root directory, a fixed target path, custom MIME type mappings, precompression enablement, request path rewriting, and lifecycle hooks for file resolution outcomes.

```typescript
export type ServeStaticOptions<E extends Env = Env> = {
  root?: string
  path?: string
  precompressed?: boolean
  mimes?: Record<string, string>
  rewriteRequestPath?: (path: string) => string
  onFound?: (path: string, c: Context<E>) => void | Promise<void>
  onNotFound?: (path: string, c: Context<E>) => void | Promise<void>
}
```

Sources: [src/middleware/serve-static/index.ts:13-21](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L13-L21)

The base `serveStatic` function wraps runtime-specific asset fetchers into a standard `MiddlewareHandler`. Because underlying engines vary, the function requires a runtime-provided `getContent` implementation alongside optional path-joining and directory-checking utilities.

| Option | Type | Default | Purpose |
| :--- | :--- | :--- | :--- |
| `root` | `string` | `'./'` | Base directory prefix for resolving asset paths |
| `path` | `string` | `undefined` | Static file path override bypassing request path inspection |
| `precompressed` | `boolean` | `undefined` | Enables checking for precompressed variants (`.br`, `.zst`, `.gz`) |
| `mimes` | `Record<string, string>` | `undefined` | Custom file extension to MIME type overrides |
| `rewriteRequestPath` | `(path: string) => string` | `undefined` | Transforms the incoming request path prior to lookup |
| `onFound` | `(path: string, c: Context<E>) => void \| Promise<void>` | `undefined` | Lifecycle callback invoked when an asset is successfully retrieved |
| `onNotFound` | `(path: string, c: Context<E>) => void \| Promise<void>` | `undefined` | Lifecycle callback invoked when asset retrieval fails |

Sources: [src/middleware/serve-static/index.ts:35-48](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L35-L48), [src/middleware/serve-static/index.ts:50-52](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L50-L52)

> [!NOTE]
> The `serveStatic` middleware is not called directly by application code under normal circumstances; runtime-specific wrappers such as those in [Bun Adapter](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/runtime-adapters/bun-adapter) and [Deno Adapter](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/runtime-adapters/deno-adapter) supply the appropriate `getContent` callback and runtime binding.

Sources: [src/middleware/serve-static/index.ts:32-34](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L32-L34)

### Supported Encoding Constants

When `precompressed` is enabled, the middleware evaluates client support against three compression algorithms defined by the `ENCODINGS` constant map, checking file extensions in strict order.

```typescript
const ENCODINGS = {
  br: '.br',
  zstd: '.zst',
  gzip: '.gz',
} as const
const ENCODINGS_ORDERED_KEYS = Object.keys(ENCODINGS) as (keyof typeof ENCODINGS)[]
```

Sources: [src/middleware/serve-static/index.ts:23-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L23-L28)

> [!IMPORTANT]
> `ENCODINGS_ORDERED_KEYS` iterates through Brotli (`br`), Zstd (`zstd`), and Gzip (`gzip`) sequentially, meaning the first matching precompressed file extension found on disk takes precedence over subsequent alternatives.

Sources: [src/middleware/serve-static/index.ts:23-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L23-L28), [src/middleware/serve-static/index.ts:104-116](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L104-L116)

## Path Normalization and Route Rewriting

### URI Decoding and Security Validation

When a static file path is not explicitly configured via `options.path`, the middleware extracts the filename from the request path using `tryDecodeURI(c.req.path)`. To prevent directory traversal attacks, the decoded string is immediately tested against a security regular expression that detects relative path components and illegal slash sequences.

```typescript
filename = tryDecodeURI(c.req.path)
if (/(?:^|[\/\\])\.{1,2}(?:$|[\/\\])|[\/\\]{2,}|\\/.test(filename)) {
  throw new Error()
}
```

Sources: [src/middleware/serve-static/index.ts:65-69](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L65-L69)

> [!WARNING]
> If URI decoding fails or the validation regex matches dot-segments (`.` or `..`) or backslashes (`\`), the middleware triggers `onNotFound` and delegates control to the next handler rather than leaking files outside the root directory.

Sources: [src/middleware/serve-static/index.ts:70-73](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L70-L73)

### Request Path Rewriting and Joining

Once validated, the filename can be transformed by an optional `rewriteRequestPath` function provided in `ServeStaticOptions`. The resulting path is then combined with the root directory using the `join` function, which defaults to `defaultJoin`.

```typescript
let path = join(
  root,
  !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename
)
```

Sources: [src/middleware/serve-static/index.ts:76-79](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L76-L79)

The `defaultJoin` implementation filters out empty path segments, normalizes multiple consecutive forward slashes, and resolves relative `.` and `..` segments sequentially.

```typescript
export const defaultJoin = (...paths: string[]): string => {
  let result = paths.filter((p) => p !== '').join('/')
  result = result.replace(/(?<=\/)\/+/g, '')

  const segments = result.split('/')
  const resolved = []

  for (const segment of segments) {
    if (segment === '..' && resolved.length > 0 && resolved.at(-1) !== '..') {
      resolved.pop()
    } else if (segment !== '.') {
      resolved.push(segment)
    }
  }

  return resolved.join('/') || '.'
}
```

Sources: [src/middleware/serve-static/path.ts:5-24](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/path.ts#L5-L24)

> [!NOTE]
> `defaultJoin` is designed exclusively for POSIX-style paths and does not support Windows drive letters or backslash separators. For Windows path compatibility, developers must supply a platform-aware join helper such as `join` from `node:path`.

Sources: [src/middleware/serve-static/path.ts:1-4](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/path.ts#L1-L4)

## MIME Type Resolution and Overrides

### File Extension Mapping and Lookup Mechanics

When static content is successfully retrieved from disk or key-value storage, the middleware determines its Internet media type by invoking `getMimeType(path, options.mimes)` with any custom mime mappings supplied via `options.mimes`, falling back to the default `baseMime` collection. The utility extracts file extensions using a regular expression that matches word characters following the final dot in the filename, converting the result to lowercase for case-insensitive lookup. If no custom or default extension match is found, the header falls back to `application/octet-stream`.

```typescript
export const getMimeType = (
  filename: string,
  mimes: Record<string, string> = baseMimes
): string | undefined => {
  const regexp = /\.([a-zA-Z0-9]+?)$/
  const match = filename.match(regexp)
  if (!match) {
    return
  }
  return mimes[match[1].toLowerCase()]
}
```

Sources: [src/utils/mime.ts:6-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/mime.ts#L6-L16)

Once the MIME type string is resolved, it is applied directly to the outgoing response headers under the `Content-Type` key.

```typescript
const mimeType = (options.mimes && getMimeType(path, options.mimes)) || getMimeType(path)
c.header('Content-Type', mimeType || 'application/octet-stream')
```

Sources: [src/middleware/serve-static/index.ts:93-94](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L93-L94)

> [!NOTE]
> The reverse lookup helper `getExtension(mimeType)` strips media type parameters and charset indicators before scanning the built-in MIME dictionary to locate a matching file extension.

Sources: [src/utils/mime.ts:18-26](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/mime.ts#L18-L26)

### Default MIME Type Registry

Hono embeds an extensive collection of common web MIME types in `_baseMimes`, aliased publicly as `mimes`. 

| Extension | Content-Type |
| :--- | :--- |
| `aac` | `audio/aac` |
| `avif` | `image/avif` |
| `css` | `text/css; charset=utf-8` |
| `csv` | `text/csv; charset=utf-8` |
| `html`, `htm` | `text/html; charset=utf-8` |
| `ico` | `image/x-icon` |
| `jpeg`, `jpg` | `image/jpeg` |
| `js`, `mjs` | `text/javascript; charset=utf-8` |
| `json` | `application/json` |
| `jsonld` | `application/ld+json` |
| `png` | `image/png` |
| `svg` | `image/svg+xml; charset=utf-8` |
| `txt` | `text/plain; charset=utf-8` |
| `wasm` | `application/wasm` |
| `webp` | `image/webp` |
| `woff2` | `font/woff2` |
| `xml` | `application/xml; charset=utf-8` |
| `zip` | `application/zip` |

Sources: [src/utils/mime.ts:35-92](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/mime.ts#L35-L92), [src/utils/mime.ts:28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/mime.ts#L28)

### MIME Resolution Call-Chain Walkthrough

The verified call-chain execution flow `Module -> GetMimeType` traces the exact sequence of function invocations from the ES module entry point down to MIME type resolution:

1. `module` (`src/adapter/cloudflare-workers/serve-static-module.ts:6-10`) initiates ES module mode handling by invoking the Cloudflare Workers adapter's `serveStatic`.
2. `serveStatic` (`src/adapter/cloudflare-workers/serve-static.ts:21-42`) constructs the environment's `getContent` callback and delegates execution to `baseServeStatic`.
3. `serveStatic` (`src/middleware/serve-static/index.ts:35-126`) normalizes request paths, checks directory status, retrieves asset content streams, and calls `getMimeType`.
4. `getMimeType` (`src/utils/mime.ts:6-16`) inspects file extension matches against custom or default dictionaries to assign the correct `Content-Type`.

```mermaid
sequenceDiagram
    participant Module as serve-static-module.ts
    participant CFServe as serve-static.ts (Adapter)
    participant BaseServe as serve-static.ts (Middleware)
    participant MimeUtil as mime.ts

    Module->>CFServe: module(options)
    CFServe->>BaseServe: serveStatic(options)
    BaseServe->>BaseServe: getContent(path, c)
    BaseServe->>MimeUtil: getMimeType(path, options.mimes)
    MimeUtil-->>BaseServe: mimeType string
```

Sources: [src/adapter/cloudflare-workers/serve-static-module.ts:6-10](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-workers/serve-static-module.ts#L6-L10), [src/adapter/cloudflare-workers/serve-static.ts:21-42](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/adapter/cloudflare-workers/serve-static.ts#L21-L42), [src/middleware/serve-static/index.ts:35-126](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L35-L126), [src/utils/mime.ts:6-16](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/mime.ts#L6-L16)

## Directory Index and Fallback Handling

### Missing Asset Propagation and Lifecycle Hooks

When a requested resource resolves to a directory path on disk or storage, Hono intercepts the path check via the optional `isDir` function. If `options.isDir` evaluates to a truthy value for the target path, the middleware automatically appends the default document constant to resolve index assets.

```typescript
const DEFAULT_DOCUMENT = 'index.html'
```

Sources: [src/middleware/serve-static/index.ts:30](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L30), [src/middleware/serve-static/index.ts:81-83](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L81-L83)

```typescript
if (options.isDir && (await options.isDir(path))) {
  path = join(path, DEFAULT_DOCUMENT)
}
```

Sources: [src/middleware/serve-static/index.ts:81-83](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L81-L83)

> [!TIP]
> The `isDir` check is entirely optional; runtimes that lack asynchronous directory checks can omit the function property, causing the middleware to skip directory index resolution and attempt direct file retrieval.

Sources: [src/middleware/serve-static/index.ts:47-48](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L47-L48), [src/middleware/serve-static/index.ts:81-83](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L81-L83)

When asset lookup fails to yield valid content—either because the file does not exist, the requested path triggered path traversal security checks, or directory fallback discovery came up empty—the middleware invokes the `onNotFound` lifecycle hook before passing control to downstream handlers via `next()`.

```typescript
    await options.onNotFound?.(path, c)
    await next()
    return
```

Sources: [src/middleware/serve-static/index.ts:122-124](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L122-L124)

> [!WARNING]
> If `onNotFound` is not provided in `ServeStaticOptions`, the middleware gracefully falls through to the next handler in the Hono application stack without emitting a terminal 404 response directly.

Sources: [src/middleware/serve-static/index.ts:19-21](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L19-L21), [src/middleware/serve-static/index.ts:122-123](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L122-L123)

## Response Creation and Asset Streaming

### Precompression and Content Negotiation

When a static asset is successfully retrieved, Hono checks whether precompression is enabled via `options.precompressed` and whether the content type is compressible using `COMPRESSIBLE_CONTENT_TYPE_REGEX`. If these conditions are met, the middleware inspects the `Accept-Encoding` request header to negotiate a compressed representation.

```typescript
const ENCODINGS = {
  br: '.br',
  zstd: '.zst',
  gzip: '.gz',
} as const
const ENCODINGS_ORDERED_KEYS = Object.keys(ENCODINGS) as (keyof typeof ENCODINGS)[]
```

Sources: [src/middleware/serve-static/index.ts:23-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L23-L28)

```typescript
      if (options.precompressed && (!mimeType || COMPRESSIBLE_CONTENT_TYPE_REGEX.test(mimeType))) {
        const acceptEncodingSet = new Set(
          c.req
            .header('Accept-Encoding')
            ?.split(',')
            .map((encoding) => encoding.trim())
        )

        for (const encoding of ENCODINGS_ORDERED_KEYS) {
          if (!acceptEncodingSet.has(encoding)) {
            continue
          }
          const compressedContent = (await getContent(path + ENCODINGS[encoding], c)) as Data | null

          if (compressedContent) {
            content = compressedContent
            c.header('Content-Encoding', encoding)
            c.header('Vary', 'Accept-Encoding', { append: true })
            break
          }
        }
      }
```

Sources: [src/middleware/serve-static/index.ts:96-116](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L96-L116)

> [!NOTE]
> The precompression routine iterates through encodings in a strict evaluation order (`br`, `zstd`, `gzip`), appending the corresponding file extension (`.br`, `.zst`, `.gz`) to locate prebuilt compressed variants on storage.

Sources: [src/middleware/serve-static/index.ts:23-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L23-L28), [src/middleware/serve-static/index.ts:104-108](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L104-L108)

### Lifecycle Delegation and Payload Streaming

Once content and encoding headers are finalized, the middleware invokes the optional `onFound` lifecycle callback before delegating payload delivery to the context response builder.

```typescript
      await options.onFound?.(path, c)
      return c.body(content)
```

Sources: [src/middleware/serve-static/index.ts:118-119](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L118-L119)

| Return Type / Condition | Handling Mechanism | Sources |
| :--- | :--- | :--- |
| `content instanceof Response` | Clones or delegates using `c.newResponse(content.body, content)` | [src/middleware/serve-static/index.ts:88-90](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L88-L90) |
| `content` (Truthy Data) | Assigns `Content-Type`, negotiates compression, calls `onFound`, and returns `c.body(content)` | [src/middleware/serve-static/index.ts:92-120](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L92-L120) |
| Falsy / Missing Content | Invokes `onNotFound`, calls `next()`, and falls through | [src/middleware/serve-static/index.ts:122-124](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L122-L124) |

Sources: [src/middleware/serve-static/index.ts:88-124](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/middleware/serve-static/index.ts#L88-L124)

## Related

- [Static Site Generation](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/helpers-and-tooling/static-site-generation)
- [ETag and Cache](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/traffic-middleware/etag-and-cache)


## Sitemap

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