Express Cheatsheet
Serving Static Files
Use this Express reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
express.static()
Serves files from a directory. Built into Express — no separate install.
const path = require('path'); // Serve files from ./public app.use(express.static('public')); // → GET /logo.png serves public/logo.png // Best practice: absolute path (immune to cwd changes) app.use(express.static(path.join(__dirname, 'public')));
Mount Path Prefix
// Serve files under /static URL prefix app.use('/static', express.static(path.join(__dirname, 'public'))); // → GET /static/logo.png serves public/logo.png // → GET /logo.png → 404 // Multiple static directories (searched in order) app.use(express.static(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'uploads')));
Options Reference
app.use(express.static(path.join(__dirname, 'public'), { dotfiles: 'ignore', // 'allow' | 'deny' | 'ignore' (default 'ignore') etag: true, // enable ETag generation (default true) extensions: ['html', 'htm'], // try these extensions if no extension given fallthrough: true, // pass to next() on 404 (default true); false = 404 error immutable: false, // add immutable directive to Cache-Control (use with maxAge) index: 'index.html', // directory index file; false to disable lastModified: true, // set Last-Modified header (default true) maxAge: 0, // Cache-Control max-age in ms or string ('1d', '2h') redirect: true, // redirect /dir to /dir/ for directories (default true) setHeaders: null, // function to set custom headers }));
setHeaders Example
app.use(express.static('public', { setHeaders: (res, filePath, stat) => { if (filePath.endsWith('.html')) { res.set('Cache-Control', 'no-cache'); } else { res.set('Cache-Control', 'public, max-age=31536000, immutable'); } res.set('X-Content-Type-Options', 'nosniff'); }, }));
Caching Strategies
// Development: no caching app.use(express.static('public', { maxAge: 0 })); // Production: long-lived assets with content hashing // (filename includes hash: app.a1b2c3.js) app.use('/assets', express.static('dist/assets', { maxAge: '1y', immutable: true, })); // HTML files: always revalidate (users get fresh HTML) // Static assets: cache forever app.use(express.static('public', { setHeaders: (res, path) => { if (path.endsWith('.html')) { res.set('Cache-Control', 'no-cache'); } else { res.set('Cache-Control', 'public, max-age=31536000, immutable'); } }, }));
Extension Fallback
// Try /about → /about.html if /about not found app.use(express.static('public', { extensions: ['html'] }));
Index Files and SPA Fallback
// Default: serves index.html for directory requests app.use(express.static('public', { index: 'index.html' })); // SPA catch-all: serve index.html for all unmatched routes // (put AFTER API routes, with fallthrough: false or manual fallback) app.use(express.static(path.join(__dirname, 'dist'))); app.get('/{*splat}', (req, res) => { // Express 4: app.get('*', ...) res.sendFile(path.join(__dirname, 'dist', 'index.html')); });
Serving Multiple Directories
Files in the first directory take precedence over later ones.
app.use(express.static(path.join(__dirname, 'public'))); // checked first app.use(express.static(path.join(__dirname, 'node_modules/bootstrap/dist'))); // fallback
Dotfiles
// Block access to .env, .git, .htaccess etc. app.use(express.static('public', { dotfiles: 'deny' })); // → GET /.env → 403 Forbidden // Silently pass to next middleware (default) app.use(express.static('public', { dotfiles: 'ignore' })); // → GET /.env → passes to next() (likely 404)
Custom Static Middleware
For more control, stream files manually with res.sendFile():
const path = require('path'); const fs = require('fs'); app.get('/downloads/:filename', authenticate, (req, res, next) => { const safeName = path.basename(req.params.filename); // prevent path traversal const filePath = path.join(__dirname, 'private', safeName); if (!fs.existsSync(filePath)) return res.status(404).send('Not found'); res.sendFile(filePath, { headers: { 'Content-Disposition': `attachment; filename="${safeName}"` }, }, (err) => { if (err) next(err); }); });
Security Considerations
- Always use absolute paths (
path.join(__dirname, ...)) — relative paths resolve fromprocess.cwd(), which may differ when the app is launched from a different directory. - Set
dotfiles: 'deny'in production to prevent leaking.env,.git, etc. - Never serve your root directory — scope to a dedicated
public/ordist/folder. - Path traversal:
express.staticis safe against../../by default; customres.sendFilecalls must sanitize withpath.basename()or resolve and verify the path stays within the intended directory.
// Safe path validation for custom file serving const root = path.resolve(__dirname, 'files'); const requested = path.resolve(root, req.params.file); if (!requested.startsWith(root + path.sep)) { return res.status(403).send('Forbidden'); } res.sendFile(requested);