Servers and environment

Serve an HTML page using Node.js

Learn how to serve an index.html page using Node.js with no dependencies, using the http module and piping fs.createReadStream() to the response.

8 minute lesson

~~~

You can serve one HTML page with Node’s built-in HTTP and filesystem modules:

const http = require('http')
const fs = require('fs')
const path = require('path')

const indexPath = path.join(__dirname, 'index.html')

const server = http.createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost')

  if (req.method !== 'GET' || url.pathname !== '/') {
    res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' })
    return res.end('Not found')
  }

  const file = fs.createReadStream(indexPath)

  file.on('open', () => {
    res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' })
    file.pipe(res)
  })

  file.on('error', (error) => {
    console.error(error)

    if (!res.headersSent) {
      res.writeHead(500, { 'content-type': 'text/plain; charset=utf-8' })
      res.end('Cannot load page')
    } else {
      res.destroy(error)
    }
  })
})

server.listen(process.env.PORT || 3000, () => {
  const address = server.address()
  console.log(`Listening on http://localhost:${address.port}`)
})

__dirname locates the page beside this CommonJS module. A bare 'index.html' path would be resolved from process.cwd(), so starting the server from another directory could break it.

The stream avoids loading the complete file into memory before sending it. Waiting for its open event also avoids announcing 200 OK before knowing the file can be opened. Stream errors after headers begin cannot be converted into a clean 500; the response must be terminated.

This is intentionally not a general static-file server. It serves only /, so user-controlled URL paths never become filesystem paths. A naive path.join(publicDir, url.pathname) implementation can create traversal and encoding bugs unless it normalizes, confines, and validates the result. Production static servers also handle MIME types, caching, ranges, conditional requests, compression, and HEAD.

Your action is to run the server from its own directory and its parent directory, request / and /missing, then rename index.html and verify that the missing file returns 500 instead of an empty 200 or a crashed process.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →