Building a Simple HTTP Server in Python
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:
- Listens on a port (usually 80 for HTTP, 443 for HTTPS)
- Accepts incoming TCP connections
- Reads HTTP request data
- 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!