Python's http.server module provides simple HTTP servers for development and testing. One command serves files from any directory.
Quick File Server
# Serve current directory on port 8000
python -m http.server
# Custom port
python -m http.server 3000
# Bind to specific interface
python -m http.server 8000 --bind 127.0.0.1
# Serve specific directory
python -m http.server --directory /path/to/filesProgrammatic Usage
from http.server import HTTPServer, SimpleHTTPRequestHandler
server = HTTPServer(('localhost', 8000), SimpleHTTPRequestHandler)
print("Serving on http://localhost:8000")
server.serve_forever()Custom Request Handler
from http.server import HTTPServer, BaseHTTPRequestHandler
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b'<h1>Hello, World!</h1>')
server = HTTPServer(('localhost', 8000), MyHandler)
server.serve_forever()Routing
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class APIHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.respond_html('<h1>Home</h1>')
elif self.path == '/api/status':
self.respond_json({'status': 'ok'})
elif self.path.startswith('/api/users/'):
user_id = self.path.split('/')[-1]
self.respond_json({'user_id': user_id})
else:
self.send_error(404, 'Not Found')
def respond_html(self, content):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(content.encode())
def respond_json(self, data):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(data).encode())
server = HTTPServer(('localhost', 8000), APIHandler)
server.serve_forever()Handling POST Requests
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class FormHandler(BaseHTTPRequestHandler):
def do_POST(self):
# Get content length
content_length = int(self.headers['Content-Length'])
# Read body
body = self.rfile.read(content_length)
# Parse JSON
if self.headers.get('Content-Type') == 'application/json':
data = json.loads(body)
else:
data = body.decode()
print(f"Received: {data}")
# Respond
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({'received': True}).encode())
server = HTTPServer(('localhost', 8000), FormHandler)
server.serve_forever()Query Parameters
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
class QueryHandler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
# params = {'name': ['alice'], 'age': ['30']}
name = params.get('name', ['Guest'])[0]
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(f'<h1>Hello, {name}!</h1>'.encode())
# Request: http://localhost:8000/?name=Alice
server = HTTPServer(('localhost', 8000), QueryHandler)
server.serve_forever()Serving Static Files with Custom Handler
from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
class CustomStaticHandler(SimpleHTTPRequestHandler):
def __init__(self, *args, directory=None, **kwargs):
self.directory = directory or os.getcwd()
super().__init__(*args, directory=self.directory, **kwargs)
def end_headers(self):
# Add custom headers
self.send_header('X-Custom-Header', 'MyValue')
self.send_header('Cache-Control', 'no-cache')
super().end_headers()
server = HTTPServer(('localhost', 8000), CustomStaticHandler)
server.serve_forever()CORS Headers
from http.server import HTTPServer, SimpleHTTPRequestHandler
class CORSHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
super().end_headers()
def do_OPTIONS(self):
self.send_response(200)
self.end_headers()
server = HTTPServer(('localhost', 8000), CORSHandler)
server.serve_forever()Threaded Server
Handle multiple requests concurrently:
from http.server import HTTPServer, SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
daemon_threads = True
server = ThreadedHTTPServer(('localhost', 8000), SimpleHTTPRequestHandler)
print("Threaded server on http://localhost:8000")
server.serve_forever()Graceful Shutdown
from http.server import HTTPServer, SimpleHTTPRequestHandler
import signal
import sys
server = HTTPServer(('localhost', 8000), SimpleHTTPRequestHandler)
def shutdown(signum, frame):
print("\nShutting down...")
server.shutdown()
sys.exit(0)
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
print("Serving on http://localhost:8000 (Ctrl+C to stop)")
server.serve_forever()CGI Server
from http.server import HTTPServer, CGIHTTPRequestHandler
# Serves CGI scripts from cgi-bin/
server = HTTPServer(('localhost', 8000), CGIHTTPRequestHandler)
server.serve_forever()SSL/HTTPS
from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl
server = HTTPServer(('localhost', 4443), SimpleHTTPRequestHandler)
# Wrap with SSL
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('cert.pem', 'key.pem')
server.socket = context.wrap_socket(server.socket, server_side=True)
print("HTTPS server on https://localhost:4443")
server.serve_forever()When to Use
Good for:
- Quick local file serving
- Development and testing
- Simple API mocks
- Learning HTTP concepts
Not for:
- Production deployments
- High-traffic applications
- Complex routing
For production, use Flask, FastAPI, or Django.
Summary
http.server is Python's built-in HTTP server:
python -m http.serverfor instant file servingSimpleHTTPRequestHandlerfor static filesBaseHTTPRequestHandlerfor custom logic- Extend with threading, CORS, and routing as needed
Perfect for development, but reach for a proper framework in production.
React to this post: