Serving Static Files

beginner
8 min

Serving Static Files

Most web applications need to deliver files that never change per request: stylesheets, client-side JavaScript, images, fonts, and downloadable documents. Express handles this with a single built-in middleware, express.static. This lesson shows you how to set it up, how paths are resolved, and the options that matter in production.

The One-Liner

Create a folder named public in your project and register the middleware:

js
const express = require('express'); const path = require('path'); const app = express(); app.use(express.static(path.join(__dirname, 'public'))); app.listen(3000, () => console.log('Static server on port 3000'));

Now any file inside public is served automatically. If public/styles.css exists, a request to /styles.css returns it with the correct Content-Type header. No route definitions required.

Why path.join and __dirname?

Passing a bare 'public' works only when you start the server from the project folder. If you launch it from elsewhere, Node resolves the relative path against your current working directory and files mysteriously 404. Building the path from __dirname pins it to the file location, which is robust no matter where the process starts.

A Practical Layout

A typical project looks like this:

project/ index.js public/ index.html css/style.css js/app.js images/logo.png

With the middleware in place, /index.html, /css/style.css, and /images/logo.png all resolve. Even better, a request to / automatically serves public/index.html because index.html is the default index file.

Linking Assets Correctly

Inside your HTML, reference assets with root-relative paths. The public folder itself never appears in URLs:

html
<link rel="stylesheet" href="/css/style.css"> <script src="/js/app.js" defer></script> <img src="/images/logo.png" alt="Logo">

A very common beginner mistake is writing /public/css/style.css. That returns 404 because express.static mounts the contents of the folder at the root, not the folder name.

Virtual Path Prefixes

Sometimes you want assets under a URL prefix, for example /static. Pass the prefix as the first argument to app.use:

js
app.use('/static', express.static(path.join(__dirname, 'public')));

Now public/css/style.css is served at /static/css/style.css. Prefixes are useful when your API routes and asset URLs might otherwise collide.

Multiple Folders and Precedence

You can register several static folders. Express checks them in order and serves the first file it finds:

js
app.use(express.static(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'uploads')));

If both folders contain report.pdf, the one in public wins because it was registered first, consistent with the general middleware ordering rule.

Caching for Production

By default files are re-sent on every request. In production, tell browsers to cache them with the maxAge option:

js
app.use(express.static('public', { maxAge: '7d', etag: true, }));

This sets a Cache-Control header so repeat visitors load assets from their local cache, cutting bandwidth and load times dramatically. Pair long cache lifetimes with versioned filenames like app.v2.js so updates still reach users.

Key Takeaways

  • express.static(folder) serves an entire directory with one line.
  • Always build the folder path with path.join(__dirname, ...) for reliability.
  • The folder name never appears in URLs; index.html is served at /.
  • Use a virtual prefix like /static to namespace assets.
  • Enable maxAge caching in production for faster repeat visits.

Next lesson: Handling POST Requests and JSON Bodies, where clients finally start sending data to the server.

Serving Static Files - Express.js | CodeYourCraft | CodeYourCraft