Skip to content
oRPC
Esc
navigateopen⌘Jpreview
On this page

Static File Plugin

Use StaticFileHandlerPlugin to serve static files alongside your procedures with standard HTTP semantics: ETag and Last-Modified conditional requests, range requests, index files, and directory traversal protection.

Installation

npm install @orpc/node@beta
pnpm add @orpc/node@beta
yarn add @orpc/node@beta
bun add @orpc/node@beta

How It Works

After routing, when no procedure matches a GET or HEAD request, the plugin maps the request path to a file inside rootDir and serves it. Matched procedures always take precedence. Requests that resolve to a directory are redirected to their trailing slash form and answered with the directory’s index.html.

Every file response carries a weak ETag and Last-Modified header, so clients sending If-None-Match or If-Modified-Since receive 304 Not Modified when the file is unchanged. Single range requests are answered with 206 Partial Content, which enables media seeking and resumable downloads.

Dot segments like .. are resolved in URL space and clamped at the served directory, following the RFC 3986 normalization browsers and proxies apply, so a request can never read outside rootDir. Dotfiles are treated as not found unless explicitly enabled.

Setup

The plugin reads files through the Node.js filesystem API but only interacts with the handler through standard oRPC interfaces, so it works with any handler on a Node.js compatible runtime, whether it uses the Node HTTP Adapter or the Fetch API Adapter.

import { StaticFileHandlerPlugin } from '@orpc/node'
import { RPCHandler } from '@orpc/server/node'

const handler = new RPCHandler(router, {
  plugins: [
    new StaticFileHandlerPlugin({
      /**
       * The directory files are served from. Resolved against the working
       * directory when relative.
       */
      rootDir: './public',

      /**
       * The URL path files are served under, appended to the handler prefix
       * when one is set.
       *
       * @default '/'
       */
      path: '/',

      /**
       * The file served when the request path resolves to a directory.
       * Set to `false` to disable directory index files.
       *
       * @default 'index.html'
       */
      indexFile: 'index.html',

      /**
       * A file served with status 200 when no file matches the request path,
       * relative to `rootDir`. Useful for single-page application routing.
       *
       * @default undefined
       */
      fallbackFile: 'index.html',

      /**
       * The `Cache-Control` response header value. Set to `false` to omit the header.
       *
       * @default 'public, max-age=0'
       */
      cacheControl: 'public, max-age=0',

      /**
       * Whether files and directories whose name starts with a dot can be served.
       *
       * @default false
       */
      dotfiles: false,

      /**
       * Whether precompressed sidecar files (`.br`, `.zst`, `.gz`) can be served
       * when the client accepts their encoding and the content type is compressible.
       *
       * @default false
       */
      precompressed: false,

      /**
       * Extra content types keyed by lowercase file extension without the dot,
       * merged over the built-in mapping. Unknown extensions are served as
       * `application/octet-stream`.
       */
      mimeTypes: {},
    }),
  ],
})

Learn More

For implementation details, see the source code.

Last updated on August 8, 2026

Was this page helpful?