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/files

Programmatic 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.server for instant file serving
  • SimpleHTTPRequestHandler for static files
  • BaseHTTPRequestHandler for 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: