HTTP Module - Serving Static Files

HTTP Module - Serving Static Files

HTTP Module - Serving Static Files

Introduction

In web development, static files refer to files that do not change dynamically and are served to the client exactly as they are stored. These files include HTML, CSS, JavaScript, images (JPG, PNG, SVG), fonts, and other resources. When building web servers with Node.js, serving static files is a core capability, especially when not using frameworks like Express.js. Node.js’s built-in http module along with the fs (File System) module can be used to serve static content efficiently.

This document walks through everything you need to know about serving static files using the native HTTP module in Node.js. You’ll learn how to serve HTML, CSS, JavaScript, images, handle file types and MIME types, implement error handling, and build a basic static file server from scratch.

What Are Static Files?

Static files are resources that the server sends to the browser without modification. Common examples include:

  • HTML files
  • CSS stylesheets
  • JavaScript files
  • Images and icons
  • Fonts and media files

Required Core Modules

http Module

To create an HTTP server and listen for requests.

fs Module

To read files from the filesystem.

path Module

To resolve and normalize file paths for security and compatibility.

mime Module (Optional for MIME Types)

To determine the correct Content-Type header based on file extension.

Creating a Simple Static File Server

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

const server = http.createServer((req, res) => {
    let filePath = path.join(__dirname, 'public', req.url === '/' ? 'index.html' : req.url);
    
    const ext = path.extname(filePath);
    let contentType = 'text/html';

    switch (ext) {
        case '.js':
            contentType = 'text/javascript';
            break;
        case '.css':
            contentType = 'text/css';
            break;
        case '.json':
            contentType = 'application/json';
            break;
        case '.png':
            contentType = 'image/png';
            break;
        case '.jpg':
        case '.jpeg':
            contentType = 'image/jpeg';
            break;
        case '.svg':
            contentType = 'image/svg+xml';
            break;
        default:
            contentType = 'text/html';
    }

    fs.readFile(filePath, (err, content) => {
        if (err) {
            if (err.code === 'ENOENT') {
                fs.readFile(path.join(__dirname, 'public', '404.html'), (err, content) => {
                    res.writeHead(404, { 'Content-Type': 'text/html' });
                    res.end(content, 'utf8');
                });
            } else {
                res.writeHead(500);
                res.end(`Server Error: ${err.code}`);
            }
        } else {
            res.writeHead(200, { 'Content-Type': contentType });
            res.end(content, 'utf8');
        }
    });
});

server.listen(3000, () => console.log('Server running on port 3000'));

Project Structure

project/
β”‚
β”œβ”€β”€ server.js
└── public/
    β”œβ”€β”€ index.html
    β”œβ”€β”€ about.html
    β”œβ”€β”€ 404.html
    β”œβ”€β”€ css/
    β”‚   └── style.css
    β”œβ”€β”€ js/
    β”‚   └── script.js
    └── images/
        └── logo.png

Serving HTML Files

When the user accesses the root ("/"), the server should serve an HTML file such as index.html:

// Inside server.js

if (req.url === '/') {
    filePath = path.join(__dirname, 'public', 'index.html');
}

Serving CSS Files

CSS files require proper MIME type and must be in the correct folder, e.g., /css/style.css:

case '.css':
    contentType = 'text/css';
    break;

Serving JavaScript Files

JavaScript files are usually located in a /js/ folder and served with:

case '.js':
    contentType = 'text/javascript';
    break;

Serving Images

To serve images like PNG, JPG, and SVG correctly, set their appropriate MIME types and read them without utf8 encoding:

case '.png':
    contentType = 'image/png';
    break;
case '.jpg':
case '.jpeg':
    contentType = 'image/jpeg';
    break;
case '.svg':
    contentType = 'image/svg+xml';
    break;

Reading Images (No utf8)

fs.readFile(filePath, (err, content) => {
    res.writeHead(200, { 'Content-Type': contentType });
    res.end(content);
});

Handling 404 Not Found

If a file is not found (ENOENT), serve a custom 404 page:

if (err.code === 'ENOENT') {
    fs.readFile(path.join(__dirname, 'public', '404.html'), (err, content) => {
        res.writeHead(404, { 'Content-Type': 'text/html' });
        res.end(content, 'utf8');
    });
}

Preventing Path Traversal Attacks

Always sanitize paths using path.normalize and ensure they don't go outside the public directory:

let safePath = path.normalize(path.join(__dirname, 'public', req.url));
if (!safePath.startsWith(path.join(__dirname, 'public'))) {
    res.writeHead(403);
    return res.end('Access Denied');
}

Serving JSON Data

If your static content includes .json files:

case '.json':
    contentType = 'application/json';
    break;

Sample JSON Serving

fs.readFile('./public/data.json', (err, data) => {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(data);
});

Using mime-types Package (Optional)

Instead of writing switch cases, you can install and use the mime-types module:

npm install mime-types
const mime = require('mime-types');

let contentType = mime.lookup(filePath) || 'application/octet-stream';

Caching Static Files

You can set cache headers for static resources:

res.writeHead(200, {
    'Content-Type': contentType,
    'Cache-Control': 'max-age=3600'
});

Serving Gzipped Files (Advanced)

const zlib = require('zlib');

fs.readFile(filePath, (err, content) => {
    zlib.gzip(content, (err, result) => {
        res.writeHead(200, {
            'Content-Encoding': 'gzip',
            'Content-Type': contentType
        });
        res.end(result);
    });
});

Security Considerations

  • Restrict access to system directories
  • Prevent access to hidden or sensitive files
  • Use HTTPS for secure content delivery

Performance Tips

  • Use caching for assets
  • Minify and bundle CSS and JavaScript
  • Compress files using gzip
  • Use a CDN in production

Real-World Use Case: Portfolio Website

Many developers host portfolio websites using only static files like HTML, CSS, JS, and images. Using the http module, you can deploy and serve this content efficiently from a Node.js server.

Example:

// /public/index.html
// /public/css/styles.css
// /public/images/photo.jpg
// /public/js/main.js

Serving static files using Node.js's native http module is a powerful and educational way to understand how file servers work. While frameworks like Express simplify this process, mastering the native method provides deeper insight and gives you complete control over routing, MIME types, error handling, and file security.

In this guide, you learned how to build a basic static file server, handle different content types, prevent security vulnerabilities, use MIME types, and improve performance with caching and compression. Whether you're hosting a landing page, documentation, or building your first Node.js site, these techniques are essential for creating efficient and reliable static file servers.

Beginner 5 Hours
HTTP Module - Serving Static Files

HTTP Module - Serving Static Files

Introduction

In web development, static files refer to files that do not change dynamically and are served to the client exactly as they are stored. These files include HTML, CSS, JavaScript, images (JPG, PNG, SVG), fonts, and other resources. When building web servers with Node.js, serving static files is a core capability, especially when not using frameworks like Express.js. Node.js’s built-in http module along with the fs (File System) module can be used to serve static content efficiently.

This document walks through everything you need to know about serving static files using the native HTTP module in Node.js. You’ll learn how to serve HTML, CSS, JavaScript, images, handle file types and MIME types, implement error handling, and build a basic static file server from scratch.

What Are Static Files?

Static files are resources that the server sends to the browser without modification. Common examples include:

  • HTML files
  • CSS stylesheets
  • JavaScript files
  • Images and icons
  • Fonts and media files

Required Core Modules

http Module

To create an HTTP server and listen for requests.

fs Module

To read files from the filesystem.

path Module

To resolve and normalize file paths for security and compatibility.

mime Module (Optional for MIME Types)

To determine the correct Content-Type header based on file extension.

Creating a Simple Static File Server

const http = require('http'); const fs = require('fs'); const path = require('path'); const server = http.createServer((req, res) => { let filePath = path.join(__dirname, 'public', req.url === '/' ? 'index.html' : req.url); const ext = path.extname(filePath); let contentType = 'text/html'; switch (ext) { case '.js': contentType = 'text/javascript'; break; case '.css': contentType = 'text/css'; break; case '.json': contentType = 'application/json'; break; case '.png': contentType = 'image/png'; break; case '.jpg': case '.jpeg': contentType = 'image/jpeg'; break; case '.svg': contentType = 'image/svg+xml'; break; default: contentType = 'text/html'; } fs.readFile(filePath, (err, content) => { if (err) { if (err.code === 'ENOENT') { fs.readFile(path.join(__dirname, 'public', '404.html'), (err, content) => { res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(content, 'utf8'); }); } else { res.writeHead(500); res.end(`Server Error: ${err.code}`); } } else { res.writeHead(200, { 'Content-Type': contentType }); res.end(content, 'utf8'); } }); }); server.listen(3000, () => console.log('Server running on port 3000'));

Project Structure

project/ │ ├── server.js └── public/ ├── index.html ├── about.html ├── 404.html ├── css/ │ └── style.css ├── js/ │ └── script.js └── images/ └── logo.png

Serving HTML Files

When the user accesses the root ("/"), the server should serve an HTML file such as index.html:

// Inside server.js if (req.url === '/') { filePath = path.join(__dirname, 'public', 'index.html'); }

Serving CSS Files

CSS files require proper MIME type and must be in the correct folder, e.g., /css/style.css:

case '.css': contentType = 'text/css'; break;

Serving JavaScript Files

JavaScript files are usually located in a /js/ folder and served with:

case '.js': contentType = 'text/javascript'; break;

Serving Images

To serve images like PNG, JPG, and SVG correctly, set their appropriate MIME types and read them without utf8 encoding:

case '.png': contentType = 'image/png'; break; case '.jpg': case '.jpeg': contentType = 'image/jpeg'; break; case '.svg': contentType = 'image/svg+xml'; break;

Reading Images (No utf8)

fs.readFile(filePath, (err, content) => { res.writeHead(200, { 'Content-Type': contentType }); res.end(content); });

Handling 404 Not Found

If a file is not found (ENOENT), serve a custom 404 page:

if (err.code === 'ENOENT') { fs.readFile(path.join(__dirname, 'public', '404.html'), (err, content) => { res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(content, 'utf8'); }); }

Preventing Path Traversal Attacks

Always sanitize paths using path.normalize and ensure they don't go outside the public directory:

let safePath = path.normalize(path.join(__dirname, 'public', req.url)); if (!safePath.startsWith(path.join(__dirname, 'public'))) { res.writeHead(403); return res.end('Access Denied'); }

Serving JSON Data

If your static content includes .json files:

case '.json': contentType = 'application/json'; break;

Sample JSON Serving

fs.readFile('./public/data.json', (err, data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(data); });

Using mime-types Package (Optional)

Instead of writing switch cases, you can install and use the mime-types module:

npm install mime-types
const mime = require('mime-types'); let contentType = mime.lookup(filePath) || 'application/octet-stream';

Caching Static Files

You can set cache headers for static resources:

res.writeHead(200, { 'Content-Type': contentType, 'Cache-Control': 'max-age=3600' });

Serving Gzipped Files (Advanced)

const zlib = require('zlib'); fs.readFile(filePath, (err, content) => { zlib.gzip(content, (err, result) => { res.writeHead(200, { 'Content-Encoding': 'gzip', 'Content-Type': contentType }); res.end(result); }); });

Security Considerations

  • Restrict access to system directories
  • Prevent access to hidden or sensitive files
  • Use HTTPS for secure content delivery

Performance Tips

  • Use caching for assets
  • Minify and bundle CSS and JavaScript
  • Compress files using gzip
  • Use a CDN in production

Real-World Use Case: Portfolio Website

Many developers host portfolio websites using only static files like HTML, CSS, JS, and images. Using the http module, you can deploy and serve this content efficiently from a Node.js server.

Example:

// /public/index.html // /public/css/styles.css // /public/images/photo.jpg // /public/js/main.js

Serving static files using Node.js's native http module is a powerful and educational way to understand how file servers work. While frameworks like Express simplify this process, mastering the native method provides deeper insight and gives you complete control over routing, MIME types, error handling, and file security.

In this guide, you learned how to build a basic static file server, handle different content types, prevent security vulnerabilities, use MIME types, and improve performance with caching and compression. Whether you're hosting a landing page, documentation, or building your first Node.js site, these techniques are essential for creating efficient and reliable static file servers.

Related Tutorials

Frequently Asked Questions for Node.js

A function passed as an argument and executed later.

Runs multiple instances to utilize multi-core systems.

Reusable blocks of code, exported and imported using require() or import.

nextTick() executes before setImmediate() in the event loop.

Starts a server and listens on specified port.

Node Package Manager β€” installs, manages, and shares JavaScript packages.

A minimal and flexible web application framework for Node.js.

A stream handles reading or writing data continuously.

It processes asynchronous callbacks and non-blocking I/O operations efficiently.

Node.js is a JavaScript runtime built on Chrome's V8 engine for server-side scripting.

An object representing the eventual completion or failure of an asynchronous operation.

require is CommonJS; import is ES6 syntax (requires transpilation or newer versions).

Use module.exports or exports.functionName.

Variables stored outside the code for configuration, accessed using process.env.


MongoDB, often used with Mongoose for schema management.

Describes project details and manages dependencies and scripts.

Synchronous blocks execution; asynchronous runs in background without blocking.

Allows or restricts resources shared between different origins.

Use try-catch, error events, or middleware for error handling.

Provides file system-related operations like read, write, delete.

Using event-driven architecture and non-blocking I/O.

Functions in Express that execute during request-response cycle.

A set of routes or endpoints to interact with server logic or databases.

Yes, it's single-threaded but handles concurrency using the event loop and asynchronous callbacks.

Middleware to parse incoming request bodies, like JSON or form data.

line

Copyrights © 2024 letsupdateskills All rights reserved