Simple Web-Server: A Beginner’s Guide

Build a Simple Web-Server in 10 MinutesCreating a simple web server is one of the most practical ways to learn how the web works. In this guide you’ll build a minimal, fast, and secure server in under ten minutes using Node.js. You’ll see the core concepts — handling HTTP requests, serving files, setting content types, and basic error handling — without frameworks getting in the way. This article includes step-by-step instructions, code you can copy, explanations of each part, and small improvements to make the server production-friendlier.


What you’ll need

  • Node.js installed (v14 or later recommended)
  • A terminal/command prompt
  • A basic text editor (VS Code, Sublime, etc.)
  • A folder for the project

If Node.js isn’t installed, download it from nodejs.org and follow the installer.


Project setup (1 minute)

  1. Create a folder for the project and open a terminal there:
    
    mkdir simple-web-server cd simple-web-server 
  2. Initialize a minimal Node project (optional but helpful for later):
    
    npm init -y 

Step 1 — Minimal HTTP server (2 minutes)

Create a file named server.js and add the following code:

const http = require('http'); const PORT = process.env.PORT || 3000; const server = http.createServer((req, res) => {   res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });   res.end('Hello from your simple web server! '); }); server.listen(PORT, () => {   console.log(`Server is listening on http://localhost:${PORT}`); }); 

Run it:

node server.js 

Open http://localhost:3000 in your browser — you should see the greeting.

Explanation:

  • http.createServer provides the request/response callback.
  • writeHead sets status and headers.
  • res.end sends the response body.

Step 2 — Serve static files (3 minutes)

Replace server.js with a version that serves files from a public folder:

const http = require('http'); const fs = require('fs'); const path = require('path'); const PORT = process.env.PORT || 3000; const PUBLIC_DIR = path.join(__dirname, 'public'); const mimeTypes = {   '.html': 'text/html; charset=utf-8',   '.css': 'text/css; charset=utf-8',   '.js': 'application/javascript; charset=utf-8',   '.json': 'application/json; charset=utf-8',   '.png': 'image/png',   '.jpg': 'image/jpeg',   '.svg': 'image/svg+xml',   '.txt': 'text/plain; charset=utf-8', }; const server = http.createServer((req, res) => {   let safePath = path.normalize(decodeURI(req.url)).replace(/^(..[/\])+/, '');   if (safePath === '/' || safePath === '') safePath = '/index.html';   const filePath = path.join(PUBLIC_DIR, safePath);   fs.stat(filePath, (err, stats) => {     if (err || !stats.isFile()) {       res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });       return res.end('404 Not Found ');     }     const ext = path.extname(filePath).toLowerCase();     const contentType = mimeTypes[ext] || 'application/octet-stream';     res.writeHead(200, { 'Content-Type': contentType });     const stream = fs.createReadStream(filePath);     stream.pipe(res);     stream.on('error', () => {       res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });       res.end('500 Internal Server Error ');     });   }); }); server.listen(PORT, () => {   console.log(`Server running at http://localhost:${PORT}/`); }); 

Create a public folder and a basic index.html:

mkdir public 

public/index.html:

<!doctype html> <html> <head><meta charset="utf-8"><title>Simple Web-Server</title></head> <body>   <h1>It works!</h1>   <p>This page is served by your simple web server.</p> </body> </html> 

Reload http://localhost:3000 — the HTML page should appear.


Step 3 — Add caching headers and logging (1–2 minutes)

Improve performance and debugging by adding basic caching and request logs. Update the file-serving response section:

// inside fs.stat callback, before creating stream: const maxAge = 3600; // seconds res.writeHead(200, {   'Content-Type': contentType,   'Cache-Control': `public, max-age=${maxAge}` }); console.log(`${new Date().toISOString()} - ${req.method} ${req.url} 200`); 

For errors:

console.log(`${new Date().toISOString()} - ${req.method} ${req.url} 404`); 

Small production tips (brief)

  • Use a reverse proxy (Nginx) to handle TLS, gzip, and static caching.
  • Run behind a process manager (pm2, systemd) to auto-restart.
  • Limit request body size and validate URLs to reduce attack surface.
  • Use helmet-like headers (Content-Security-Policy, X-Content-Type-Options) if serving dynamic content.

Troubleshooting

  • PORT in use: choose another port or kill the process that’s using it.
  • Files not found: check PUBLIC_DIR path and filename case-sensitivity on your OS.
  • Binary files corrupted: ensure you stream files and don’t modify encoding.

  • Add routing to serve dynamic content or APIs.
  • Explore Express.js for middleware and routing.
  • Learn HTTPS with the built-in tls module or let a reverse proxy handle certificates.

You now have a lightweight, readable web server that serves static files, supports basic MIME types, caching, and logging. It’s a practical base for learning and small projects — extend it as your needs grow.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *