connect-static-file
raw JSON →connect-static-file is a lightweight middleware for Connect and Express applications designed to serve a single static file. Unlike `express.static` which serves entire directories, this module focuses on efficiently delivering a specified file, making it ideal for specific assets like `index.html` or a `robots.txt`. The current stable version is 2.0.0, with its last major update occurring in 2017, suggesting a low-cadence, maintenance-focused project. A key differentiator is its `encoded` option, allowing developers to serve pre-compressed files (e.g., gzip) directly, optimizing client delivery by avoiding on-the-fly compression. It also provides fine-grained control over HTTP caching headers like `ETag`, `Last-Modified`, and `Cache-Control` (`maxAge`).
Common errors
error Error: ENOENT: no such file or directory, stat '/path/to/non-existent-file.txt' ↓
staticFile('/your/path', ...) is correct and the file exists at that location with proper read permissions for the Node.js process. error Cannot GET /my-asset (HTTP 404 response) ↓
app.use('/your-url', staticFile(...)) exactly matches the path clients are requesting. If using the encoded option, verify a non-encoded fallback middleware is provided immediately after the encoded version for browsers that don't support the encoding. error Client receives uncompressed file despite `encoded: 'gzip'` in configuration, or `Content-Encoding` header is missing/incorrect. ↓
Accept-Encoding header. If it includes 'gzip', ensure your middleware order is correct with the encoded version preceding any unencoded fallback for the same route. Double-check the spelling and value of the encoded option. Warnings
breaking Version 2.0.0 removed support for Node.js v0.10. If you are on an older Node.js runtime, you must use `connect-static-file@1.x`. ↓
breaking Version 2.0.0 removed the `acceptRanges` option, which was previously enabled by default. This change should have minimal impact as the option offered little value. ↓
gotcha When using the `encoded` option (e.g., `encoded: 'gzip'`), `connect-static-file` will only serve the pre-encoded file if the client's `Accept-Encoding` header supports it. If not, the request is passed to the next middleware. You MUST implement a fallback middleware to serve an unencoded version or an alternative encoding if the client doesn't support the specified `encoded` type. ↓
gotcha This middleware is designed to serve a *single* specific file per instance. It does not perform directory lookups or serve multiple files based on a path segment. Attempts to use it like `express.static` for entire directories will fail. ↓
gotcha The `extensions` option is disabled by default (`false`) and is ignored if the requested URL already includes a file extension. It only applies if the initial file `path` cannot be found and the URL has no explicit extension (e.g., `/my-page` rather than `/my-page.html`). ↓
Install
npm install connect-static-file yarn add connect-static-file pnpm add connect-static-file Imports
- staticFile wrong
import staticFile from 'connect-static-file';correctconst staticFile = require('connect-static-file'); - staticFile wrong
import { staticFile } from 'connect-static-file';correctconst staticFile = require('connect-static-file'); - Middleware Integration wrong
app.get('/my-file.txt', staticFile('path/to/actual/file.txt'));correctapp.use('/my-file.txt', staticFile('path/to/actual/file.txt'));
Quickstart
const express = require('express');
const staticFile = require('connect-static-file');
const path = require('path');
const fs = require('fs');
const app = express();
const port = 3000;
// Create a dummy file for the example
const dummyFilePath = path.join(__dirname, 'hello.txt');
fs.writeFileSync(dummyFilePath, 'Hello from connect-static-file!');
// Serve the dummy file at /hello.txt
app.use('/hello.txt', staticFile(dummyFilePath, {
maxAge: '1d', // Cache for 1 day
headers: { 'X-Powered-By': 'connect-static-file' }
}));
// Serve a pre-gzipped version of a bundle if supported, with a fallback
const gzippedBundlePath = path.join(__dirname, 'bundle.js.gz');
const unzippedBundlePath = path.join(__dirname, 'bundle.js');
fs.writeFileSync(unzippedBundlePath, 'console.log("This is the uncompressed bundle.");');
// In a real app, you'd generate gzippedBundlePath via a build step
fs.writeFileSync(gzippedBundlePath, Buffer.from('\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xcbH\xcd\xc9\xc9W(\xcf/\xcaIQ\x04\x00\x9d\x1a\xa0*\x1e\x00\x00\x00', 'binary')); // Minimal gzip for 'Hello'
app.use('/bundle.js', staticFile(gzippedBundlePath, { encoded: 'gzip', maxAge: '7d' }));
app.use('/bundle.js', staticFile(unzippedBundlePath, { maxAge: '7d' })); // Fallback
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
console.log(`Try accessing: http://localhost:${port}/hello.txt`);
console.log(`Try accessing: http://localhost:${port}/bundle.js (will serve gzip if supported by browser)`);
});
// Cleanup dummy files on process exit
process.on('exit', () => {
fs.unlinkSync(dummyFilePath);
fs.unlinkSync(gzippedBundlePath);
fs.unlinkSync(unzippedBundlePath);
});