Of course! Let's dive deep into the http.server module in Python, specifically focusing on the BaseHTTPRequestHandler and SimpleHTTPRequestHandler classes, which are the core components for creating a simple HTTP server.

What is http.server?
The http.server module in Python's standard library provides a basic HTTP server framework. It's perfect for:
- Quickly serving files from a directory (like a simple web server for development).
- Building a custom API with minimal setup for testing or prototyping.
- Learning how HTTP requests and responses work at a fundamental level.
The two main classes you'll interact with are:
http.server.BaseHTTPRequestHandler: The base class. You inherit from this to create your own custom handler, giving you full control over how to process requests and generate responses.http.server.SimpleHTTPRequestHandler: A convenient, ready-to-use class that inherits fromBaseHTTPRequestHandler. It's designed to serve files from the current directory and its subdirectories.
Part 1: The Simple Way - SimpleHTTPRequestHandler
This is the quickest way to get a file server running. You don't need to write any Python code if you just use the command line.
Command-Line Usage
Open your terminal, navigate to the directory you want to serve, and run:

# Python 3 python -m http.server # Or specify a port (e.g., port 8001) python -m http.server 8001
Now, open your web browser and go to http://localhost:8000. You will see a directory listing of the folder you ran the command in. You can click on files to view them or download them.
Programmatic Usage
You can also use SimpleHTTPRequestHandler within a Python script.
# simple_server.py
import http.server
import socketserver
# Define the port you want to use
PORT = 8000
# Create a handler. SimpleHTTPRequestHandler will serve files
# from the current directory.
Handler = http.server.SimpleHTTPRequestHandler
# Create a server instance with the handler and the port
# The '' for the server address means it will listen on all available interfaces
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving at port {PORT}")
# The serve_forever() method starts the server and keeps it running
httpd.serve_forever()
To run this:
- Save the code as
simple_server.py. - Create a file named
index.htmlin the same directory with some content (e.g.,<h1>Hello from Python!</h1>). - Run the script:
python simple_server.py. - Open your browser to
http://localhost:8000. You will see yourindex.htmlfile.
Part 2: The Powerful Way - BaseHTTPRequestHandler
This is where you can build your own custom logic. By subclassing BaseHTTPRequestHandler, you can define what happens for different HTTP methods (GET, POST, etc.) and different paths.

The key is to override the do_GET(), do_POST(), etc., methods.
Example: A Custom "Hello World" API
Let's create a server that responds to different paths with different messages.
# custom_server.py
import http.server
import socketserver
import json
# Define the port
PORT = 8080
# Create a custom handler by inheriting from BaseHTTPRequestHandler
class MyCustomHandler(http.server.BaseHTTPRequestHandler):
# This method is called for every GET request
def do_GET(self):
# 1. Determine the path from the request
path = self.path
# 2. Set the response status code and headers
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
# 3. Prepare the response body based on the path
if path == '/':
response_message = {"message": "Welcome to the Custom API!"}
elif path == '/hello':
response_message = {"message": "Hello, World!"}
elif path == '/status':
response_message = {"status": "OK", "service": "Custom API"}
else:
# If the path is not found, send a 404 error
self.send_response(404)
self.send_header('Content-type', 'application/json')
self.end_headers()
response_message = {"error": "Not Found"}
# 4. Send the response body
# json.dumps converts the Python dictionary to a JSON string
self.wfile.write(json.dumps(response_message).encode('utf-8'))
# Create the server
with socketserver.TCPServer(("", PORT), MyCustomHandler) as httpd:
print(f"Serving custom API on port {PORT}")
print("Try accessing:")
print(f" - http://localhost:{PORT}/")
print(f" - http://localhost:{PORT}/hello")
print(f" - http://localhost:{PORT}/status")
print(f" - http://localhost:{PORT}/unknown")
httpd.serve_forever()
How to use this example:
-
Save the code as
custom_server.py. -
Run it from your terminal:
python custom_server.py. -
Use your browser or a tool like
curlto test the endpoints:# Terminal 1: Run the server python custom_server.py # Terminal 2: Use curl to test the endpoints curl http://localhost:8080/hello # Output: {"message": "Hello, World!"} curl http://localhost:8080/status # Output: {"service": "Custom API", "status": "OK"} curl http://localhost:8080/unknown # Output: {"error": "Not Found"}
Breakdown of BaseHTTPRequestHandler Key Attributes and Methods
When you override a do_METHOD function, you have access to several useful attributes and methods of the self object:
self.command: The HTTP command (e.g.,'GET','POST').self.path: The path requested by the client (e.g.,'/api/data').self.headers: An instance ofhttp.client.HTTPMessagecontaining the request headers. You can access them like a dictionary:self.headers['User-Agent'].self.client_address: A tuple(ip_address, port)of the client.self.rfile: A file-like object for reading the request body (used in POST requests).self.wfile: A file-like object for writing the response body.
Essential Methods for Building a Response:
self.send_response(code): Sets the HTTP status code (e.g.,200,404,500).self.send_header(keyword, value): Adds a response header. You must call this for each header you want to send.self.end_headers(): Sends a blank line, signaling the end of the headers and the start of the body. This is required.self.wfile.write(data): Writes data to the response body. The data must be in bytes, so you often need to encode it (e.g.,.encode('utf-8')).
Part 3: Handling POST Requests and Request Bodies
Handling POST requests is slightly more complex because you need to read the data from the client.
# post_server.py
import http.server
import socketserver
import json
PORT = 9090
class PostHandler(http.server.BaseHTTPRequestHandler):
def do_POST(self):
# 1. Get the content length of the incoming data
content_length = int(self.headers['Content-Length'])
# 2. Read the POST data from the request body
# self.rfile.read() reads the specified number of bytes
post_data = self.rfile.read(content_length)
# 3. Decode the data from bytes to a string
# Assuming the data is JSON, but it could be form data, etc.
try:
data = json.loads(post_data.decode('utf-8'))
print(f"Received data: {data}")
except json.JSONDecodeError:
data = {"error": "Invalid JSON"}
# 4. Send a response
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {"status": "success", "received": data}
self.wfile.write(json.dumps(response).encode('utf-8'))
with socketserver.TCPServer(("", PORT), PostHandler) as httpd:
print(f"Server ready to accept POST requests on port {PORT}")
httpd.serve_forever()
How to test this POST server:
-
Run the server:
python post_server.py. -
Use
curlto send a POST request with a JSON body:curl -X POST \ -H "Content-Type: application/json" \ -d '{"name": "Alice", "age": 30}' \ http://localhost:9090
Server Output:
Received data: {'name': 'Alice', 'age': 30}
Client Output:
{"received": {"name": "Alice", "age": 30}, "status": "success"}
Summary
| Feature | SimpleHTTPRequestHandler |
BaseHTTPRequestHandler (Custom) |
|---|---|---|
| Purpose | Serve static files from a directory. | Build custom, dynamic web applications/APIs. |
| Setup | Very easy, one-line command or class instantiation. | Requires subclassing and overriding do_METHOD methods. |
| Control | Low. Serves files based on the URL path. | Full control over request routing, logic, and response generation. |
| Use Case | Quick file sharing, development server. | Prototyping APIs, building microservices, learning HTTP. |
