📄 Serving HTML Files using the Node.js http Module

Introduction

One of the most common tasks when building a web server is serving HTML pages to web browsers. Using Node.js's built-in http module along with the fs (File System) module, you can read HTML files from your project and send them as HTTP responses. This approach forms the foundation of traditional server-side web applications before using frameworks such as Express.

Information

The http module handles HTTP requests, while the fs module is responsible for reading files from the file system.

Learning Objectives

  • Create an HTTP server.
  • Read HTML files using the fs module.
  • Send HTML content to the browser.
  • Serve multiple HTML pages using routing.
  • Handle missing files and server errors gracefully.

Project Structure

📁 my-website
📄 server.js
📄 index.html
📄 about.html
📄 contact.html
📄 404.html

Creating a Simple HTML File

index.html

<!DOCTYPE html>
<html>
<head>
  <title>Home</title>
</head>
<body>
  <h1>Welcome to My Website</h1>
  <p>This page is served using Node.js.</p>
</body>
</html>

Serving a Single HTML File

Use the fs.readFile() method to read an HTML file asynchronously and send its contents to the client.

server.js

const http = require("http");
const fs = require("fs");

const server = http.createServer((req, res) => {

  fs.readFile("index.html", (err, data) => {

    if (err) {
      res.writeHead(500, {
        "Content-Type": "text/plain"
      });

      return res.end("Internal Server Error");
    }

    res.writeHead(200, {
      "Content-Type": "text/html"
    });

    res.end(data);

  });

});

server.listen(3000, () => {
  console.log("Server running at http://localhost:3000");
});

How It Works

🌍 Browser Requests a Page
📡 HTTP Server Receives the Request
📂 Read HTML File Using fs.readFile()
File Read Successfully
File Read Fails
📄 Send HTML Content
❌ Return 500 Error

Serving Multiple HTML Pages

Combine routing with file reading to serve different HTML pages for different URLs.

Serving multiple pages

const http = require("http");
const fs = require("fs");

const server = http.createServer((req, res) => {

  let filePath = "";

  if (req.url === "/") {
    filePath = "index.html";
  } else if (req.url === "/about") {
    filePath = "about.html";
  } else if (req.url === "/contact") {
    filePath = "contact.html";
  } else {
    filePath = "404.html";
  }

  fs.readFile(filePath, (err, data) => {

    if (err) {

      res.writeHead(500, {
        "Content-Type": "text/plain"
      });

      return res.end("Internal Server Error");
    }

    const statusCode = filePath === "404.html" ? 404 : 200;

    res.writeHead(statusCode, {
      "Content-Type": "text/html"
    });

    res.end(data);

  });

});

server.listen(3000);

Routing Flow

Incoming Request
URL = "/"
URL = "/about"
URL = "/contact"
Any Other URL
Serve index.html
Serve about.html
Serve contact.html
Serve 404.html

Understanding fs.readFile()

ParameterDescription
pathPath to the file to be read.
callbackFunction executed after reading the file.
errContains an error if the operation fails.
dataContains the file contents as a Buffer by default.

Reading Files as Text

If you want the file contents as a string instead of a Buffer, specify an encoding such as utf8.

Read file with UTF-8 encoding

fs.readFile("index.html", "utf8", (err, data) => {

  if (err) {
    return;
  }

  console.log(data);

});

HTTP Status Codes

Status CodeMeaningUsage
200OKHTML page served successfully.
404Not FoundRequested page does not exist.
500Internal Server ErrorUnable to read the requested file.

Testing the Server

  1. Create the required HTML files.
  2. Run the server using node server.js.
  3. Open http://localhost:3000 in a browser.
  4. Visit /about and /contact.
  5. Request an unknown URL to verify the custom 404 page.

Advantages

  • No external dependencies are required.
  • Simple and easy to understand.
  • Ideal for learning HTTP servers and file handling.
  • Works well for small websites and educational projects.

Limitations

  • Manual routing becomes difficult as applications grow.
  • Each request reads files from disk unless caching is implemented.
  • No built-in support for templates or layouts.
  • Static assets such as CSS and JavaScript require additional routing.

Warning

Reading files from disk for every request is suitable for learning and small applications, but larger applications often use caching or dedicated static file middleware to improve performance.

Best Practices

  • Always set the correct Content-Type header.
  • Handle file read errors gracefully.
  • Return a custom 404 page for unknown routes.
  • Use asynchronous file operations to avoid blocking the event loop.
  • Organize HTML files in a dedicated directory such as public or views as the project grows.

Best Practice

Use asynchronous methods like fs.readFile() instead of synchronous file operations in HTTP request handlers. This keeps the server responsive and allows it to handle multiple client requests efficiently.

Official Documentation

Node.js HTTP Module Documentation and Node.js File System (fs) Module Documentation

Summary

Summary

Serving HTML files with the Node.js http module involves reading HTML files using the fs module, setting the Content-Type header to text/html, and sending the file contents as the HTTP response. By combining routing, asynchronous file handling, and proper error management, you can build simple multi-page websites while gaining a strong understanding of how web servers deliver content to browsers.