Python Central HubSign in

Python Programming / Python Networking

Building a Simple HTTP Server

Create a basic HTTP server in Python using http.server and understand request/response basics.

The built-in http.server

Python includes a simple HTTP server in the standard library.

Great for:

  • local demos
  • serving static files

Not ideal for production.

Serve a folder

If you run:

Shell
python -m http.server 8000

Then open:

  • http://localhost:8000

Custom handler

Python
from http.server import BaseHTTPRequestHandler, HTTPServer
 
 
class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Type", "text/plain")
        self.end_headers()
        self.wfile.write(b"Hello from Python HTTP server")
 
 
if __name__ == "__main__":
    server = HTTPServer(("127.0.0.1", 8000), Handler)
    print("Serving on http://127.0.0.1:8000")
    server.serve_forever()

What’s happening?

  • do_GET handles HTTP GET requests
  • send_response sets status code
  • headers must be sent before the body
diagram what http.server answers, and what it refuses mermaid
SimpleHTTPRequestHandler maps a URL path onto the current working directory and serves whatever it finds. It generates a listing for a directory, returns 404 for a missing file, and refuses every method except GET and HEAD. All four responses below were fetched from a server actually running.
sketch Four requests to a running http.server p5.js
A server was started on a temporary directory containing one file, and each of these requests was actually made. The responses are what came back, including the 501 for POST -- the built-in handler implements only GET and HEAD, so it is a file viewer rather than a web framework.

Requests and responses

HTTP is a conversation made of text. The client sends a request: a method (GET, POST), a path (/hello), headers and sometimes a body. The server sends back a response: a status code (200 for success, 404 for not found), headers, and a body. BaseHTTPRequestHandler parses the incoming request for you and calls a method named after the HTTP verb: do_GET for GET, do_POST for POST, and so on. Your job is to write the response in the right order: status line, headers, blank line, body.

Reading the custom handler

In the example, send_response(200) writes the status line. send_header("Content-Type", "text/plain") tells the browser how to read the body. end_headers() writes the blank line that separates headers from the body, and self.wfile.write(...) sends the body. The body must be bytes, which is why the string is written as b"...". If you skip end_headers() the browser sees a broken response.

HTTPServer(("127.0.0.1", 8000), Handler) binds to the local machine only, which is what you want for experiments. serve_forever() then loops, handling one request at a time. Stop it with Ctrl+C.

Reacting to the path

Inside do_GET, self.path holds what the client asked for. This example returns JSON for /api and a plain message otherwise:

Python
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
 
 
class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/api":
            body = json.dumps({"status": "ok"}).encode("utf-8")
            content_type = "application/json"
            code = 200
        else:
            body = b"Not found"
            content_type = "text/plain"
            code = 404
        self.send_response(code)
        self.send_header("Content-Type", content_type)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)
 
 
if __name__ == "__main__":
    HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()

Visit http://127.0.0.1:8000/api to see the JSON and any other path to see the 404. Sending Content-Length lets the client know when the body ends.

Common mistakes

  • Writing a str to wfile. It needs bytes; use .encode("utf-8").
  • Sending the body before the headers. Headers always come first and end with end_headers().
  • Serving the wrong folder. python -m http.server shares the current directory, including files you did not mean to expose.
  • Binding to 0.0.0.0 on a shared network and exposing it to others.
  • Forgetting other methods. A handler with only do_GET answers a POST with an error.

When to use it

Use http.server for local previews of static sites, quick demos, and learning how HTTP works. For real applications, use a framework such as Flask or FastAPI behind a production server.

Check yourself

quizCheck yourself
  1. `python -m http.server` serves which directory?

    Show answer

    B — The current working directory — Whatever folder you started it in. Run it in the wrong place and it will serve your source, your `.env` and anything else that is there.

  2. What does a POST to the built-in handler return?

    Show answer

    D — 501 Unsupported method — Verified. `SimpleHTTPRequestHandler` implements only GET and HEAD, so it is a file viewer rather than a framework — to handle POST you subclass it and write `do_POST`.

  3. You request a path that is a directory. What comes back?

    Show answer

    C — 200 with a generated listing (or index.html if present) — Measured: 200 with a 299-byte generated listing. If an `index.html` exists it is served instead, which is what makes this useful for previewing a static site.

  4. Why should this server never be exposed beyond localhost?

    Show answer

    B — No authentication, no TLS, and its root is whatever directory you happened to be in — It also binds all interfaces by default, so on a shared network the whole directory is readable. Bind it to 127.0.0.1 and treat it as a local preview tool.

🧪 Try It Yourself

Exercise 1 – HTTP GET with urllib

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 2 – Socket Connection

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 3 – Resolve a Hostname

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Was this page helpful?

Let us know how we did