Serving Static Files

`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.

3 min read Level 1/5 #express#static#files
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.

Templating →