Feb 04, 2026 10 min read

Building a Simple HTTP Server in Python

Python Networking HTTP Tutorial

Ever wondered what happens when you type a URL into your browser? Behind every website is a web server listening for incoming requests and sending back responses. Let’s build a simple one from scratch using nothing but Python’s built-in socket library.

The Basics

At its core, an HTTP server is just a program that:

  1. Listens on a port (usually 80 for HTTP, 443 for HTTPS)
  2. Accepts incoming TCP connections
  3. Reads HTTP request data
  4. Sends back an HTTP response

Setting Up the Socket

First, we need to create a socket and bind it to an address and port:

import socket

# Create a TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Allow reuse of address (helps with debugging)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind to localhost on port 8080
server_socket.bind(('localhost', 8080))

# Listen for incoming connections (backlog of 5)
server_socket.listen(5)

print("Server listening on http://localhost:8080")

Accepting Connections

Now we’ll create a loop to accept incoming connections:

while True:
    # Accept a new connection
    client_socket, client_address = server_socket.accept()
    print(f"Connection from {client_address}")
    
    # Handle the request
    handle_request(client_socket)

Parsing HTTP Requests

HTTP requests follow a specific format. Let’s parse them:

def handle_request(client_socket):
    # Receive data (max 4096 bytes)
    request_data = client_socket.recv(4096).decode('utf-8')
    
    if not request_data:
        client_socket.close()
        return
    
    # Parse the request line
    lines = request_data.split('\r\n')
    request_line = lines[0]
    method, path, version = request_line.split(' ')
    
    print(f"{method} {path}")
    
    # Generate response based on path
    response = generate_response(path)
    
    # Send response
    client_socket.sendall(response.encode('utf-8'))
    client_socket.close()

Generating Responses

Now let’s create some basic responses:

def generate_response(path):
    if path == '/':
        body = """<!DOCTYPE html>
<html>
<head><title>My Server</title></head>
<body>
    <h1>Hello from my Python server!</h1>
    <p>Try visiting <a href="/about">/about</a></p>
</body>
</html>"""
        return create_http_response(200, "OK", body)
    
    elif path == '/about':
        body = """<!DOCTYPE html>
<html>
<head><title>About</title></head>
<body>
    <h1>About This Server</h1>
    <p>Built with Python sockets!</p>
</body>
</html>"""
        return create_http_response(200, "OK", body)
    
    else:
        body = """<!DOCTYPE html>
<html>
<head><title>404</title></head>
<body>
    <h1>404 - Page Not Found</h1>
    <p>The page you're looking for doesn't exist.</p>
</body>
</html>"""
        return create_http_response(404, "Not Found", body)

def create_http_response(status_code, status_text, body):
    response_line = f"HTTP/1.1 {status_code} {status_text}\r\n"
    headers = f"Content-Type: text/html\r\n"
    headers += f"Content-Length: {len(body)}\r\n"
    headers += "Connection: close\r\n"
    headers += "\r\n"
    
    return response_line + headers + body

The Complete Server

Putting it all together:

import socket

def create_http_response(status_code, status_text, body):
    response_line = f"HTTP/1.1 {status_code} {status_text}\r\n"
    headers = f"Content-Type: text/html\r\n"
    headers += f"Content-Length: {len(body)}\r\n"
    headers += "Connection: close\r\n"
    headers += "\r\n"
    return response_line + headers + body

def generate_response(path):
    if path == '/':
        body = """<!DOCTYPE html>
<html><body><h1>Hello!</h1></body></html>"""
        return create_http_response(200, "OK", body)
    else:
        body = """<!DOCTYPE html>
<html><body><h1>404</h1></body></html>"""
        return create_http_response(404, "Not Found", body)

def handle_request(client_socket):
    request_data = client_socket.recv(4096).decode('utf-8')
    if not request_data:
        client_socket.close()
        return
    
    lines = request_data.split('\r\n')
    method, path, version = lines[0].split(' ')
    
    response = generate_response(path)
    client_socket.sendall(response.encode('utf-8'))
    client_socket.close()

def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('localhost', 8080))
    server_socket.listen(5)
    
    print("Server running on http://localhost:8080")
    print("Press Ctrl+C to stop")
    
    try:
        while True:
            client_socket, address = server_socket.accept()
            handle_request(client_socket)
    except KeyboardInterrupt:
        print("\nShutting down server...")
        server_socket.close()

if __name__ == "__main__":
    main()

Testing It Out

Save the code to server.py and run it:

python server.py

Then open your browser and visit http://localhost:8080. You should see your “Hello!” message.

What We Learned

  • TCP Sockets: The foundation of internet communication
  • HTTP Protocol: Request/response format with headers and body
  • Status Codes: 200 for success, 404 for not found, etc.
  • Blocking I/O: Our simple server handles one request at a time

Next Steps

This is just the beginning! Real web servers add:

  • Multi-threading: Handle multiple requests simultaneously
  • Static file serving: Return actual files from disk
  • Routing: Map URLs to different handlers
  • Security: HTTPS, input validation, rate limiting

Understanding these fundamentals makes frameworks like Flask or Express less magical—you know what’s happening under the hood.

Happy coding!

← Back to Home