Serving Static Files & HTTPS
📖 Concept
Serving static files (HTML, CSS, JS, images) and enabling HTTPS are fundamental skills for building production web servers.
Static file serving:
The raw http module doesn't have built-in static file serving — you must implement it manually by reading files and setting appropriate MIME types.
MIME types matter:
Browsers use the Content-Type header to determine how to handle a response. Sending an HTML file with text/plain renders it as text, not a page.
| Extension | MIME Type |
|---|---|
.html |
text/html |
.css |
text/css |
.js |
application/javascript |
.json |
application/json |
.png |
image/png |
.jpg |
image/jpeg |
.svg |
image/svg+xml |
.pdf |
application/pdf |
.woff2 |
font/woff2 |
HTTPS:
Production servers require HTTPS. Node.js provides the https module which works identically to http but requires an SSL/TLS certificate.
Security headers: Every production server should include security headers:
Content-Security-Policy— Prevent XSS attacksX-Content-Type-Options: nosniff— Prevent MIME-type sniffingX-Frame-Options: DENY— Prevent clickjackingStrict-Transport-Security— Enforce HTTPS
🏠 Real-world analogy: Serving static files is like a library (the building). Books (files) are organized on shelves (directories), and each book has a classification (MIME type). HTTPS is like having a locked door and security guard — visitors can still read the books, but the communication is private.
💻 Code Example
1// Static File Server & HTTPS23const http = require("http");4const https = require("https");5const fs = require("fs");6const path = require("path");78// 1. MIME type mapping9const MIME_TYPES = {10 ".html": "text/html; charset=utf-8",11 ".css": "text/css",12 ".js": "application/javascript",13 ".json": "application/json",14 ".png": "image/png",15 ".jpg": "image/jpeg",16 ".jpeg": "image/jpeg",17 ".gif": "image/gif",18 ".svg": "image/svg+xml",19 ".ico": "image/x-icon",20 ".pdf": "application/pdf",21 ".woff": "font/woff",22 ".woff2": "font/woff2",23 ".ttf": "font/ttf",24 ".mp4": "video/mp4",25 ".webp": "image/webp",26};2728// 2. Static file server with security29function createStaticServer(publicDir) {30 return http.createServer(async (req, res) => {31 // Security headers32 res.setHeader("X-Content-Type-Options", "nosniff");33 res.setHeader("X-Frame-Options", "DENY");34 res.setHeader("X-XSS-Protection", "1; mode=block");3536 // Only serve GET/HEAD requests37 if (req.method !== "GET" && req.method !== "HEAD") {38 res.writeHead(405, { Allow: "GET, HEAD" });39 res.end("Method Not Allowed");40 return;41 }4243 // Parse URL and prevent directory traversal44 let filePath = path.join(publicDir, decodeURIComponent(req.url));4546 // ✅ Security: Prevent path traversal attacks47 if (!filePath.startsWith(publicDir)) {48 res.writeHead(403);49 res.end("Forbidden");50 return;51 }5253 try {54 const stats = await fs.promises.stat(filePath);5556 // Serve index.html for directories57 if (stats.isDirectory()) {58 filePath = path.join(filePath, "index.html");59 }6061 // Get MIME type62 const ext = path.extname(filePath).toLowerCase();63 const contentType = MIME_TYPES[ext] || "application/octet-stream";6465 // Caching headers66 const isAsset = [".css", ".js", ".png", ".jpg", ".woff2"].includes(ext);67 if (isAsset) {68 res.setHeader("Cache-Control", "public, max-age=31536000"); // 1 year69 } else {70 res.setHeader("Cache-Control", "no-cache");71 }7273 // Stream the file (memory efficient for large files)74 res.writeHead(200, { "Content-Type": contentType });7576 if (req.method === "HEAD") {77 res.end();78 return;79 }8081 const stream = fs.createReadStream(filePath);82 stream.pipe(res);83 stream.on("error", () => {84 res.writeHead(500);85 res.end("Internal Server Error");86 });87 } catch (err) {88 if (err.code === "ENOENT") {89 res.writeHead(404, { "Content-Type": "text/html" });90 res.end("<h1>404 — Not Found</h1>");91 } else {92 res.writeHead(500);93 res.end("Internal Server Error");94 }95 }96 });97}9899// 3. HTTPS server100function createHTTPSServer() {101 const options = {102 key: fs.readFileSync("certs/private-key.pem"),103 cert: fs.readFileSync("certs/certificate.pem"),104 // For production, also include CA chain:105 // ca: fs.readFileSync("certs/ca-chain.pem"),106 };107108 const server = https.createServer(options, (req, res) => {109 res.writeHead(200, { "Content-Type": "text/plain" });110 res.end("Secure connection established!");111 });112113 server.listen(443, () => {114 console.log("HTTPS server running on port 443");115 });116}117118// 4. HTTP → HTTPS redirect119function createRedirectServer() {120 http121 .createServer((req, res) => {122 res.writeHead(301, {123 Location: `https://${req.headers.host}${req.url}`,124 });125 res.end();126 })127 .listen(80, () => {128 console.log("HTTP redirect server on port 80");129 });130}131132// 5. Start static server133const PUBLIC_DIR = path.resolve(__dirname, "public");134const server = createStaticServer(PUBLIC_DIR);135server.listen(3000, () => {136 console.log(`Static server: http://localhost:3000 (serving ${PUBLIC_DIR})`);137});
🏋️ Practice Exercise
Exercises:
- Build a static file server that serves files from a
public/directory with proper MIME types - Add path traversal protection — ensure requests can't escape the public directory
- Implement ETag-based caching for static assets
- Create self-signed SSL certificates and start an HTTPS server
- Build an HTTP → HTTPS redirect server
- Add gzip compression for text-based responses (HTML, CSS, JS, JSON)
⚠️ Common Mistakes
Not preventing path traversal attacks —
../../etc/passwdin the URL can expose system files; always validate that the resolved path is within the public directoryUsing wrong or missing MIME types — serving JavaScript with
text/plaincauses browsers to reject it; always map file extensions to correct content typesLoading entire files into memory before sending — use
createReadStream().pipe(res)for efficient streaming, especially for large filesNot implementing HTTPS in production — all modern applications require HTTPS; use Let's Encrypt for free certificates
Serving user-uploaded files from the same origin — this enables XSS attacks; use a separate domain or CDN for user content
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Serving Static Files & HTTPS. Login to unlock this feature.