Skip to main content
Node.js Basics
CHAPTER 16 Beginner

Serving Static Files in Express

Updated: May 13, 2026
15 min read

# Serving Static Files in Express

Welcome to Chapter 16! So far, we have been sending plain text or raw HTML strings back to the browser using res.send(). But a real website has beautiful CSS styling, images, and frontend JavaScript files.

If you put an <img src="logo.png"> in your HTML, the browser will make a GET request to your server for /logo.png. If you don't have a route explicitly set up for that exact image, Express will return a 404 error, and your image will be broken. We solve this using Static Files.

---

1. Introduction

A Static File is a file that the server sends to the client exactly as it is, without modifying it. This includes:

  • HTML files (.html)
  • Stylesheets (.css)
  • Client-side JavaScript (.js files meant for the browser)
  • Images (.png, .jpg, .svg)

Instead of writing a custom app.get() route for every single image and CSS file on your computer, Express provides a built-in middleware function called express.static() to handle it automatically.

---

2. Learning Objectives

By the end of this chapter, you will be able to:

  • Understand the difference between static and dynamic files.
  • Use the built-in express.static() middleware.
  • Configure a public folder to hold your frontend assets.
  • Serve CSS files and link them to HTML.
  • Serve images and display them in the browser.

---

3. Beginner-Friendly Explanations

The public Folder

By convention, backend developers create a folder named public (or sometimes assets or static) in the root of their project.

You put all your CSS, frontend JS, and images inside this folder. Then, you tell Express: *"Hey, if anyone asks for a file, look inside the 'public' folder first. If you find it, send it to them automatically!"*

This is much easier than writing 50 different routes for 50 different images.

---

4. Syntax Explanation

Here is how you enable static file serving.

```javascript id="ch16-syntax-1" const express = require('express'); const app = express();

// Use the built-in static middleware // We tell it the name of the folder containing our assets app.use(express.static('public'));

app.listen(3000);

1234567891011121314151617181920
**Output Explanation:**
If you have a file located at `public/styles.css` on your hard drive, you can now open Chrome and visit `http://localhost:3000/styles.css` to view it. 
*Notice that the URL does NOT include the word "public".* Express treats the contents of the public folder as the root directory of your website.

---

## 5. Real-world Examples

**Why is this important?**
If you are building a traditional website (not a Single Page Application like React), the user's browser needs to download your CSS to make the site look good. 

If you look at the network tab on any major website, you will see requests for hundreds of static files (logos, fonts, stylesheets). Backend servers use highly optimized static file servers (or CDNs) to deliver these quickly. `express.static()` is the Node.js way of achieving this.

---

## 6. Multiple Code Examples

### Example 1: Absolute Paths for Safety
Just like we learned in Chapter 6, relying on relative paths (`'public'`) can break if you run your node server from a different directory. It is best practice to use the `path` module.

javascript id="ch16-code-1" const express = require('express'); const path = require('path'); const app = express();

// Safe, absolute path static serving app.use(express.static(path.join(__dirname, 'public')));

app.listen(3000);

123
### Example 2: Serving HTML files automatically
If you put an `index.html` file inside the `public` folder, Express is smart enough to automatically serve that file when a user visits the root route `/`. You don't even need to write an `app.get('/')` route!

javascript id="ch16-code-2" // If public/index.html exists, visiting http://localhost:3000/ // will automatically display it. app.use(express.static(path.join(__dirname, 'public')));

123
### Example 3: Virtual Path Prefix
Sometimes you don't want your static files at the root. You can create a "virtual" prefix.

javascript id="ch16-code-3" // Files in the public folder are now accessed via /assets/... app.use('/assets', express.static(path.join(__dirname, 'public')));

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
Now, to access `public/style.css`, the user must visit `http://localhost:3000/assets/style.css`.

---

## 7. Output Explanations

What happens when there is a route conflict?
Imagine you have a file `public/about.html`, AND you also have a route `app.get('/about.html', (req, res) => res.send('Backend override'))`. 

Which one wins? **The one that is declared first in the code.**
Because `app.use(express.static)` is usually at the top, Express will find the file in the public folder, send it, and the `app.get` route will never run!

---

## 8. Common Mistakes

1. **Including "public" in the HTML link:** 
   ```html
   <!-- BAD (Will 404 error) -->
   <link rel="stylesheet" href="/public/style.css">
   
   <!-- GOOD -->
   <link rel="stylesheet" href="/style.css">
   ```
2. **Forgetting the leading slash in HTML:** Writing `href="style.css"` instead of `href="/style.css"`. If you are on a nested route like `/blog/article/5`, the browser will look for the CSS at `/blog/article/style.css` which is wrong. The `/` ensures it always looks at the root.
3. **Leaving secrets in the public folder:** Never put files containing passwords, API keys, or database configs in the `public` folder. Anyone on the internet can download anything inside the public folder!

---

## 9. Best Practices

- **Folder Structure:** Organize your `public` folder logically. Create subfolders: `public/css`, `public/js` (for frontend scripts), and `public/images`.
- **Absolute Paths:** Always use `path.join(__dirname, 'public')` to prevent path resolution bugs when deploying to a remote server.
- **Use `res.sendFile` for specific HTML:** If you have HTML pages outside the public folder (e.g., in a `views` folder), use `res.sendFile(path.join(__dirname, 'views', 'about.html'))` inside an `app.get` route.

---

## 10. Exercises

1. Create a `public` folder. Inside it, create `style.css` and give the `body` a background color.
2. Inside `public`, create `index.html`. Link the stylesheet using `<link rel="stylesheet" href="/style.css">`.
3. Create `server.js`, set up `express.static`, and run the server. Visit localhost and verify the background color works!

---

## 11. Mini Project: Static portfolio site

**Objective:** Host a complete frontend static website using Express.

**Step 1:** Folder Structure

text static-portfolio/ │── server.js └── public/ ├── index.html ├── css/ │ └── main.css └── images/ └── profile.png (put any image here)

1
**Step 2:** The HTML (`public/index.html`)

html id="ch16-mini-project-html" <!DOCTYPE html> <html> <head> <title>My Portfolio</title> <link rel="stylesheet" href="/css/main.css"> </head> <body> <div class="container"> <img src="/images/profile.png" alt="Profile Picture"> <h1>Hello, I'm a Backend Developer</h1> <p>This entire page is served using Express Static Middleware!</p> </div> </body> </html>

1
**Step 3:** The CSS (`public/css/main.css`)

css id="ch16-mini-project-css" body { background: #1a1a1a; color: white; font-family: sans-serif; text-align: center; } .container { margin-top: 50px; } img { width: 150px; border-radius: 50%; border: 3px solid #00ff88; } h1 { color: #00ff88; }

1
**Step 4:** The Server (`server.js`)

javascript id="ch16-mini-project-js" const express = require('express'); const path = require('path'); const app = express();

// Serve everything inside the public folder app.use(express.static(path.join(__dirname, 'public')));

app.listen(3000, () => { console.log("Static Portfolio running on http://localhost:3000"); });

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
**Run it:**
`node server.js` -> Visit `http://localhost:3000` to see your beautifully styled site!

---

## 12. Coding Challenges

**Challenge 1:** Add a new HTML file to your `public` folder named `projects.html`. Create a navigation bar in both HTML files so you can click back and forth between Home and Projects. Notice how Express handles the routing automatically without you writing `app.get('/projects.html')`.

**Challenge 2:** Try moving the `public` folder into a subfolder named `frontend`. Update your `server.js` using `path.join` to point to the new location.

---

## 13. MCQs with Answers

**Q1: Which files are considered "Static Files"?**
A) Database config files.
B) Files containing Express routing logic.
C) Images, CSS, and HTML files.
D) Package.json
**Answer:** C

**Q2: Which Express middleware is used to serve static files?**
A) `express.assets()`
B) `express.serve()`
C) `express.public()`
D) `express.static()`
**Answer:** D

**Q3: If you configure `app.use(express.static('public'))` and have a file `public/css/style.css`, what URL should the browser request?**
A) `http://localhost:3000/public/css/style.css`
B) `http://localhost:3000/css/style.css`
C) `http://localhost:3000/style.css`
D) `http://localhost:3000/static/style.css`
**Answer:** B

**Q4: Is it safe to put a `.env` file containing passwords inside the public folder?**
A) Yes, Express hides it.
B) No, everything in the public folder is accessible to anyone on the internet.
**Answer:** B

---

## 14. Interview Questions

1. **How does `express.static` improve server performance?**
   *Answer:* It eliminates the need to manually write routes for every single asset. It reads files directly from the file system and efficiently streams them to the client with the correct MIME types (Content-Type headers) automatically set.
2. **Explain the purpose of the `public` folder convention.**
   *Answer:* By isolating all client-side assets (images, CSS, frontend JS) into a single directory, you ensure a clear separation between backend code (which must remain secure) and frontend code (which is meant to be downloaded by browsers). 

---

## 15. FAQs

**Q: Can I have more than one static directory?**
A: Yes! You can call `express.static` multiple times. 

javascript app.use(express.static('public')); app.use(express.static('files')); `` Express will look in public first, and if it doesn't find the file, it will look in files.

Q: Why doesn't my CSS update when I refresh the page? A: Browsers aggressively cache static files to save bandwidth. If you change your CSS and don't see it, do a "Hard Refresh" (Ctrl + F5 on Windows, Cmd + Shift + R on Mac) to force the browser to download the new file.

---

16. Summary

  • Static files are assets sent directly to the client without modification.
  • Store them in a dedicated folder, conventionally named public.
  • Use app.use(express.static(path.join(__dirname, 'public'))) to serve them.
  • Do NOT include the word "public" in your HTML links (e.g., use /css/style.css`).
  • Never put sensitive backend code inside the static folder.

---

17. Next Chapter Recommendation

Serving static HTML is great for simple portfolios. But what if you want to show a user their username when they log in? You can't do that with static HTML. We need dynamic HTML! In Chapter 17: Express.js Templating Engines, we will learn how to use EJS to inject JavaScript variables directly into HTML before sending it to the user.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·