`express.static` — Photos, CSS, JS, Anything On Disk
Serving Static Files
One line of middleware serves a whole directory of static files — safely and with the right Content-Type.
What you'll learn
- Mount express.static
- Configure caching
- Know when to serve from a CDN instead
To serve files from disk, mount express.static — built-in
middleware that handles everything: Content-Type, ranges,
ETags, security.
Basic
import express from "express";
import path from "node:path";
const app = express();
app.use(express.static(path.join(import.meta.dirname, "public"))); A file at public/logo.png is served at /logo.png.
Mount Under a Path
app.use("/assets", express.static("public"));
// public/logo.png → /assets/logo.png Caching
app.use(express.static("public", {
maxAge: "1y", // Cache-Control: max-age=31536000
etag: true,
immutable: true, // only for fingerprinted assets
})); For fingerprinted filenames (e.g. app.4f2a8c.js), immutable: true
- a 1-year max-age means browsers cache forever, never re-check.
Multiple Directories
app.use(express.static("public"));
app.use(express.static("dist")); Express tries each in order — first hit wins.
index.html
express.static auto-serves index.html when the URL matches a
directory. Disable:
app.use(express.static("public", { index: false })); When NOT to Use express.static
For real production with significant static traffic, serve from:
- A CDN — Cloudflare, Fastly, CloudFront. Edge cache, free TLS.
- The reverse proxy — Nginx, Caddy. Serves files faster than Node.
- Object storage with CDN — S3 + CloudFront, R2, Backblaze.
Your Node process should handle dynamic content. Static files are better elsewhere — faster, cheaper, less to manage.
Security
express.static is hardened — it won’t serve paths above the
mounted root, even with ../ tricks. Still: never mount sensitive
directories.