http.server provides a simple HTTP server for serving files and building basic web services. Great for development and quick sharing.

Command-Line Usage

# Serve current directory on port 8000
python -m http.server
 
# Custom port
python -m http.server 3000
 
# Bind to specific address
python -m http.server --bind 127.0.0.1 8000
 
# Serve specific directory
python -m http.server --directory /path/to/files
 
# IPv6
python -m http.server --bind ::

Basic Server in Code

from http.server import HTTPServer, SimpleHTTPRequestHandler
 
def run_server(port=8000):
    server_address = ("", port)
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    print(f"Serving on http://localhost:{port}")
    httpd.serve_forever()
 
if __name__ == "__main__":
    run_server()

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>")
    
    def do_POST(self):
        content_length = int(self.headers["Content-Length"])
        body = self.rfile.read(content_length)
        
        self.send_response(200)
        self.send_header("Content-type", "application/json")
        self.end_headers()
        self.wfile.write(b'{"status": "received"}')
 
httpd = HTTPServer(("", 8000), MyHandler)
httpd.serve_forever()

Routing

from http.server import HTTPServer, BaseHTTPRequestHandler
import json
 
class APIHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            self.respond(200, "text/html", b"<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.respond(404, "text/plain", b"Not Found")
    
    def respond(self, status, content_type, body):
        self.send_response(status)
        self.send_header("Content-type", content_type)
        self.end_headers()
        self.wfile.write(body)
    
    def respond_json(self, data):
        body = json.dumps(data).encode()
        self.respond(200, "application/json", body)

Handling Query Parameters

from http.server import BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
 
class QueryHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        parsed = urlparse(self.path)
        query = parse_qs(parsed.query)
        
        # /search?q=python&limit=10
        # query = {"q": ["python"], "limit": ["10"]}
        
        search_term = query.get("q", [""])[0]
        limit = int(query.get("limit", ["10"])[0])
        
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        self.wfile.write(f"Search: {search_term}, Limit: {limit}".encode())

Handling POST Data

from http.server import BaseHTTPRequestHandler
import json
from urllib.parse import parse_qs
 
class PostHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        content_type = self.headers.get("Content-Type", "")
        content_length = int(self.headers.get("Content-Length", 0))
        body = self.rfile.read(content_length)
        
        if "application/json" in content_type:
            data = json.loads(body)
        elif "application/x-www-form-urlencoded" in content_type:
            data = parse_qs(body.decode())
        else:
            data = body
        
        self.send_response(200)
        self.send_header("Content-type", "application/json")
        self.end_headers()
        self.wfile.write(json.dumps({"received": str(data)}).encode())

Serving Files

from http.server import SimpleHTTPRequestHandler, HTTPServer
import os
 
class CustomFileHandler(SimpleHTTPRequestHandler):
    def __init__(self, *args, directory=None, **kwargs):
        self.directory = directory or os.getcwd()
        super().__init__(*args, directory=self.directory, **kwargs)
    
    def do_GET(self):
        # Add custom headers
        if self.path.endswith(".js"):
            self.send_header("Content-Type", "application/javascript")
        super().do_GET()
 
# Serve from specific directory
os.chdir("/path/to/files")
httpd = HTTPServer(("", 8000), SimpleHTTPRequestHandler)
httpd.serve_forever()

CORS Headers

from http.server import SimpleHTTPRequestHandler, HTTPServer
 
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()
 
httpd = HTTPServer(("", 8000), CORSHandler)
httpd.serve_forever()

Threaded Server

from http.server import HTTPServer, SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn
 
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in separate threads."""
    daemon_threads = True
 
httpd = ThreadedHTTPServer(("", 8000), SimpleHTTPRequestHandler)
httpd.serve_forever()

HTTPS Server

from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl
 
httpd = HTTPServer(("", 443), SimpleHTTPRequestHandler)
 
# Wrap with SSL
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain("cert.pem", "key.pem")
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
 
httpd.serve_forever()

Logging

from http.server import BaseHTTPRequestHandler
import logging
 
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
 
class LoggingHandler(BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        logger.info("%s - %s", self.address_string(), format % args)
    
    def do_GET(self):
        logger.info(f"GET {self.path} from {self.client_address}")
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"OK")

Graceful Shutdown

from http.server import HTTPServer, SimpleHTTPRequestHandler
import signal
import sys
 
httpd = HTTPServer(("", 8000), SimpleHTTPRequestHandler)
 
def shutdown(signum, frame):
    print("\nShutting down...")
    httpd.shutdown()
    sys.exit(0)
 
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
 
print("Server running on http://localhost:8000")
httpd.serve_forever()

Development Server Pattern

from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
import sys
 
def serve(directory=".", port=8000, bind="127.0.0.1"):
    """Simple development server."""
    os.chdir(directory)
    
    handler = SimpleHTTPRequestHandler
    httpd = HTTPServer((bind, port), handler)
    
    print(f"Serving {os.getcwd()}")
    print(f"http://{bind}:{port}")
    print("Press Ctrl+C to stop")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\nStopped")
        httpd.shutdown()
 
if __name__ == "__main__":
    port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
    serve(port=port)

Common Patterns

from http.server import BaseHTTPRequestHandler
import json
 
class JSONAPIHandler(BaseHTTPRequestHandler):
    """Simple JSON API handler."""
    
    def send_json(self, data, status=200):
        body = json.dumps(data).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", len(body))
        self.end_headers()
        self.wfile.write(body)
    
    def read_json(self):
        length = int(self.headers.get("Content-Length", 0))
        body = self.rfile.read(length)
        return json.loads(body) if body else {}
    
    def do_GET(self):
        self.send_json({"message": "Hello", "path": self.path})
    
    def do_POST(self):
        data = self.read_json()
        self.send_json({"received": data})

Limitations

# http.server is NOT for production:
# - Single-threaded by default
# - No security hardening
# - Basic features only
# - Performance is limited
 
# For production, use:
# - Flask/FastAPI for APIs
# - nginx for static files
# - gunicorn/uvicorn as WSGI/ASGI servers

http.server is perfect for quick file sharing and development. For anything production-facing, use a proper web framework.

React to this post: