You've built a basic server with http.createServer() already, back in the Getting Started chapter. This page goes further — handling different routes and methods, reading a request body, and setting proper headers.
Reading the method and path to build simple routing
const http = require('http');
http.createServer((req, res) => {
if (req.method === 'GET' && req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Welcome home');
} else if (req.method === 'GET' && req.url === '/about') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('About this site');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Page not found');
}
}).listen(3000, () => console.log('Server on port 3000'));
This is exactly the kind of manual routing that frameworks like Express exist to simplify, as covered in the earlier Frameworks page — but seeing it built by hand once makes clear what a framework is actually doing under the hood.
Reading request headers
http.createServer((req, res) => {
console.log('User agent:', req.headers['user-agent']);
console.log('Content type:', req.headers['content-type']);
res.end('Headers logged');
}).listen(3000);
Reading a POST request body
Unlike a framework, raw http doesn't parse the request body for you — the body arrives as a stream of chunks you collect yourself:
http.createServer((req, res) => {
if (req.method === 'POST') {
let body = '';
req.on('data', (chunk) => { body += chunk; });
req.on('end', () => {
const parsed = JSON.parse(body);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ received: parsed }));
});
}
}).listen(3000);
Setting response status and headers correctly
res.writeHead(201, {
'Content-Type': 'application/json',
'X-Powered-By': 'my-node-app'
});
res.end(JSON.stringify({ created: true }));
Common status codes worth knowing: 200 (success), 201 (created), 400 (bad request from the client), 401 (not authenticated), 404 (not found), 500 (server error).
Making an outgoing HTTP request
http isn't just for building servers — it also makes requests to other servers:
const http = require('http');
http.get('http://api.example.com/data', (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
console.log(JSON.parse(data));
});
});
In practice, most projects use axios or the built-in fetch (available in modern Node.js) for outgoing requests instead, since they're considerably less verbose than manually collecting chunks — but knowing the raw mechanism underneath explains what those libraries are doing for you.
HTTP vs HTTPS
http handles plain, unencrypted connections. For encrypted traffic, the near-identical https module is used instead — same API shape, but requiring an SSL/TLS certificate, covered on the TLS/SSL page later in this chapter. In production, nearly every public-facing server uses HTTPS.