Serve Swagger UI with Python's built-in HTTP server

Hosts a self-contained Swagger UI with a mock OpenAPI spec using only Python's standard library HTTP server.

Easy Python 3.9+ Aug 9, 2026 API design & gRPC 14 views 0 copies

Python code

54 lines
Python 3.9+
from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
import tempfile

SWAGGER_HTML = """<!DOCTYPE html>
<html>
<head>
    <title>Mock Swagger UI</title>
    <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@4/swagger-ui.css">
</head>
<body>
    <div id="swagger-ui"></div>
    <script src="https://unpkg.com/swagger-ui-dist@4/swagger-ui-bundle.js"></script>
    <script>
        SwaggerUIBundle({
            url: "/openapi.json",
            dom_id: "#swagger-ui",
        });
    </script>
</body>
</html>
"""

OPENAPI_JSON = """{
  "openapi": "3.0.0",
  "info": {"title": "Mock API", "version": "1.0.0"},
  "paths": {
    "/health": {
      "get": {"summary": "Health check", "responses": {"200": {"description": "OK"}}}
    }
  }
}"""

class MockHandler(SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(SWAGGER_HTML.encode())
        elif self.path == "/openapi.json":
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(OPENAPI_JSON.encode())
        else:
            super().do_GET()

if __name__ == "__main__":
    with tempfile.TemporaryDirectory() as tmpdir:
        os.chdir(tmpdir)
        server = HTTPServer(("localhost", 8080), MockHandler)
        print("Serving Swagger UI on http://localhost:8080")
        server.serve_forever()

Output

stdout
Serving Swagger UI on http://localhost:8080

When you open http://localhost:8080 in a browser, the page renders the Swagger UI with the /health endpoint from openapi.json.

How it works

The SimpleHTTPRequestHandler subclass overrides do_GET to serve two custom routes: / returns an HTML page that loads the Swagger UI from a CDN, and /openapi.json returns a minimal OpenAPI 3.0 spec. By calling super().do_GET() for any other path, the handler falls back to serving static files from the current directory, which we switch to a temporary directory using os.chdir. This keeps the mock entirely self-contained without needing Flask or FastAPI. The HTTPServer with serve_forever() blocks the main thread and listens on localhost:8080, making it perfect for local API contract testing.

Common mistakes

  • Forgetting to call `super().do_GET()` for unknown paths, which breaks static file serving.
  • Not setting the correct Content-Type header (text/html vs application/json), causing browsers to render raw text.
  • Using `http.server` without a temporary directory, leaving stray files in the project.
  • Binding to a non-loopback address without proper firewall rules, exposing the server publicly.

Variations

  1. Use `functools.partial` to pass configurable paths or specs to the handler class.
  2. Serve from an in-memory dict of routes instead of hard-coded globals for larger specs.

Real-world use cases

  • Local API contract validation while developing a client SDK against a mocked OpenAPI spec.
  • Spinning up a lightweight mock service for frontend teams to prototype against before the backend is ready.
  • Testing API documentation generation tools by pointing them at a temporary OpenAPI endpoint.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from API design & gRPC

Related tutorials and quizzes for this topic.