python

Understanding HTTP Server by implementing in Python

Build a basic HTTP server in Python with the socket module to see how it works: accept a connection, read the request, send headers and a response, close it.

I’ve been programming professionally for about three years, and I’ve used Python for about four. I started with it in my sophomore year of college, and I’ve since used it for web applications, data analysis, and automation scripts.

This post builds a simple HTTP server in Python, so you can see how request handling, response formatting, and sockets actually work under the hood.

An HTTP server is a loop: accept a connection, read the request, write a response, close it.

An HTTP server accepts requests from a client, usually a browser, and sends back the resource it asked for. It sits between the browser and whatever data or files the site needs to serve.

Knowing how this works helps you write better web code. It also makes the frameworks built on top of it easier to understand.

The basics of an HTTP server

An HTTP server does three things:

  1. Handling Requests

The server’s first job is handling incoming requests:

  1. Listening for Requests: The server listens on a fixed port (commonly port 80 for HTTP and port 443 for HTTPS) for incoming requests.

  2. Receiving Requests: When a client sends a request, the server reads it: the HTTP method (e.g., GET, POST), the requested resource (e.g., /index.html), headers, and body.

  3. Interpreting the Request: The server parses the URL, the method, and any parameters to work out what the client is asking for.

  4. Processing Requests

Once the server understands the request, it decides what to do with it:

  1. Routing: The server maps the request to a resource. Static content like HTML, CSS, or images is usually read straight from the file system. Dynamic content goes through server-side code (e.g., PHP, Python, Node.js) that builds the response.

  2. Executing Server-Side Logic: For dynamic requests, the server runs code to build the response: querying a database, doing calculations, or calling other services.

  3. Handling Security: The server also checks authentication and authorization, so only the right users reach protected resources.

  4. Sending Responses

After processing the request, the server sends an HTTP response back. That response has three parts:

  1. Status Line: The status line includes the HTTP version, a status code (e.g., 200 OK, 404 Not Found), and a status message. The status code indicates the result of the request, such as whether it was successful, if the resource was not found, or if there was a server error.
  2. Headers: The response headers provide additional information about the response. Common headers include Content-Type (indicating the type of content, such as text/html or application/json), Content-Length (indicating the size of the response body), and Server (providing information about the server software).
  3. Body: The body of the response contains the actual content being sent to the client. This can be an HTML document, an image, a JSON object, or any other type of web content.

Example Response

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 123
Server: Apache/2.4.1 (Unix)

<html>
<head>
   <title>Example</title>
</head>
<body>
   <h1>Hello, World!</h1>
</body>
</html>

With that covered, let’s implement a simple HTTP server in python.

Implementation in Python

Here’s a simple HTTP server in python:

python
import socket

# Define server address and port
HOST, PORT = "", 8000

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Listening on port {PORT}")

while True:
    client_connection, client_address = server_socket.accept()
    request_data = client_connection.recv(1024)
    print(request_data.decode("utf-8"))
    response_body = """
    <html>
    <head><title>Hello</title></head>
    <body><h1>Hello, World!</h1></body>
    </html>
    """
    http_response = f"""
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=UTF-8
    Content-Length: {len(response_body)}

    {response_body}
    """
    client_connection.sendall(http_response.encode("utf-8"))
    client_connection.close()

Breaking down the code

python
import socket

This imports the socket module, which gives Python low-level networking primitives for creating and managing connections. It’s part of the Python standard library, so there’s nothing to install.

  • Define server address and port

python
# Define server address and port

HOST, PORT = "", 8000
  • HOST is an empty string, so the server accepts connections on every available network interface.

  • PORT is 8000, the port the server listens on.

  • Creating socket object

python
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • This creates a new socket object.

  • socket.AF_INET specifies the address family for IPv4.

  • socket.SOCK_STREAM specifies the socket type for TCP, a connection-oriented protocol.

  • Bind the socket to the address and port

python
server_socket.bind((HOST, PORT))
  • bind attaches the socket to that network interface and port, so it’s ready to accept connections there.

  • Listen for incoming connections

python
server_socket.listen(5)
print(f"Listening on port {PORT}")
  • listen puts the socket into listening mode so it can accept incoming connections.

  • The 5 sets the maximum number of queued connections waiting to be accepted.

  • The main loop

python
while True:
    client_connection, client_address = server_socket.accept()
    request_data = client_connection.recv(1024)
    print(request_data.decode("utf-8"))
    response_body = """
    <html>
    <head><title>Hello</title></head>
    <body><h1>Hello, World!</h1></body>
    </html>
    """
    http_response = f"""
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=UTF-8
    Content-Length: {len(response_body)}

    {response_body}
    """
    client_connection.sendall(http_response.encode("utf-8"))
    client_connection.close()

The while True loop keeps the server running, handling connections one after another. client_connection, client_address = server_socket.accept() blocks until a client connects, then returns a new socket, client_connection, for talking to that client, plus its address.

request_data reads up to 1024 bytes from the client. The server then builds the HTTP response body and headers, sends them back with client_connection.sendall(http_response.encode("utf-8")), which encodes the response from a string to bytes, then closes the connection with client_connection.close().

Conclusion

That’s the core of how an HTTP server works. If you have feedback, leave a comment below, or send me an email if you’d rather not comment publicly.

You can take this further: for example, make the server look for an HTML file on disk and send it back when a request comes in.

If you enjoyed this and want to learn more stuff like this, I recommend Code Crafters. It has challenges for building Docker, building your own shell, and more.


Announcements

  • I started a YouTube channel a year ago. If you want a video series on stuff like this, subscribe and share your thoughts in this anonymous survey.
  • I’m also open for Python consulting. If you have a Python project or need tech advice, send an email and we can discuss it.
  • Thanks for reading and for supporting my work.

If this was useful, you can support my work by buying me a coffee. If you share this on X, tag me @muhammad_o7. You can also follow me on LinkedIn.

Want new posts in your inbox? Subscribe by email or follow the RSS feed.