Sirv Static File Server Middleware
Sirv is an optimized and lightweight middleware designed for serving static assets in Node.js applications, compatible with frameworks like Polka, Express, and native HTTP/S servers. The current stable version is 3.0.2. Its primary differentiator is a significant performance advantage over alternatives like `serve-static` because it pre-scans and caches file system information upfront (when not in 'dev' mode), avoiding costly per-request file system checks. This makes it very efficient for production deployments. Releases are active, with recent patches and a major version upgrade to v3.0.0 that introduced native ESM support and a higher Node.js baseline. It ships with TypeScript types, enhancing developer experience.
Common errors
-
ERR_REQUIRE_ESM: require() of ES Module ... sirv/index.mjs not supported.
cause Attempting to `require('sirv')` in a Node.js environment configured for ES Modules (`"type": "module"` in `package.json`) after upgrading to sirv v3+.fixChange `const sirv = require('sirv');` to `import sirv from 'sirv';` and ensure your project uses ES Modules correctly. -
GET /path/to/asset 404 Not Found
cause The `dir` option provided to `sirv()` is incorrect, or the requested file does not exist within that directory, or `opts.dotfiles` and `opts.extensions` are not configured to match the asset.fixVerify the `dir` path is absolute and correct. Check if `opts.dotfiles` is `true` for dotfiles, and `opts.extensions` includes the necessary fallback extensions (e.g., `['html']` for `/foo` to find `/foo.html`). Also, ensure file permissions allow Node.js to read the files. -
Error: Port 3000 is already in use
cause Another process is already listening on the port that your `polka` (or other HTTP server) instance is trying to bind to.fixChange the `PORT` variable in your quickstart code to an unused port (e.g., 8080, 5000) or ensure the conflicting process is stopped. You can also use a tool like `lsof -i :<PORT>` on Unix-like systems to identify the process.
Warnings
- breaking Sirv v3.0.0 and later require Node.js version 18 or higher. Projects running on older Node.js versions must either upgrade Node.js or remain on sirv v2.x.
- breaking Sirv v3.0.0 introduced native ESM support via an `exports` map, which changes how the package is imported. Direct `require()` statements in ESM contexts or projects with `"type": "module"` in `package.json` may break, resulting in `ERR_REQUIRE_ESM`.
- gotcha Using `opts.dev: true` enables 'dev' mode, which disables file caching and performs file system checks on every request. This is highly inefficient and should *never* be used in production environments, as it will severely degrade performance.
- gotcha The `dir` option in `sirv(dir, opts)` expects a directory path. While it resolves relative paths based on `process.cwd()`, relying on `process.cwd()` can be brittle in complex deployment scenarios. Incorrect paths can lead to assets not being found.
- gotcha Sirv v3.0.2 includes a patch to prevent serving top-level files that unexpectedly share a name with the configured static directory. If you previously relied on such a pattern for some edge cases, this behavior will now be prevented.
Install
-
npm install sirv -
yarn add sirv -
pnpm add sirv
Imports
- sirv
const sirv = require('sirv');import sirv from 'sirv';
- SirvMiddlewareFunction
import type { SirvOptions } from 'sirv'; - sirv
import sirv from 'sirv';
Quickstart
import polka from 'polka';
import sirv from 'sirv';
import compression from 'compression';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const publicDir = path.join(__dirname, 'public');
// Create a dummy 'public' directory and an index.html file for demonstration
import { promises as fs } from 'node:fs';
async function setupPublicDir() {
await fs.mkdir(publicDir, { recursive: true });
await fs.writeFile(path.join(publicDir, 'index.html'), '<h1>Hello from Sirv!</h1><p>This is a static file.</p>');
await fs.writeFile(path.join(publicDir, 'styles.css'), 'body { font-family: sans-serif; background-color: #f0f0f0; }');
}
// Initialize sirv handler
const assets = sirv(publicDir, {
maxAge: 31536000, // 1 year
immutable: true,
gzip: true, // Look for precompiled .gz files
dotfiles: false, // Don't serve dotfiles
dev: process.env.NODE_ENV === 'development' // Enable dev mode for caching control
});
const PORT = process.env.PORT ?? 3000;
await setupPublicDir();
polka()
.use(compression()) // Apply compression before sirv
.use(assets) // Serve static assets
.use('/api', (req, res) => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ message: 'This is an API endpoint', timestamp: Date.now() }));
})
.listen(PORT, (err) => {
if (err) throw err;
console.log(`> Ready on http://localhost:${PORT}!`);
console.log(`> Serving static files from ${publicDir}`);
});