Writing a Custom HTTP Server from Scratch in Python
Creating an HTTP server from scratch in Python is a great way to understand how web servers handle client requests and responses. Instead of using Python’s built-in http.server
module, we will build a basic HTTP server using sockets that can process requests, serve static files, and respond to different HTTP methods.
Step 1: Setting Up the Server Socket
The first step is to create a socket that listens for incoming connections. The socket will bind to a specific IP address and port, allowing clients (such as web browsers) to connect and send requests.
Implementation:
import socket
# Server Configuration
HOST = '127.0.0.1' # Localhost
PORT = 8080 # Port to listen on
# Create a socket object
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the address and port
server_socket.bind((HOST, PORT))
# Listen for incoming connections
server_socket.listen(5)
print(f"Server is running on http://{HOST}:{PORT}")
# Accept and handle client connections
while True:
client_socket, client_address = server_socket.accept()
print(f"Connection from {client_address}")
request_data = client_socket.recv(1024).decode()
print("Request Data:\n", request_data)
# Close the client connection
client_socket.close()
Explanation:
- A TCP socket is created using
socket.AF_INET
(IPv4) andsocket.SOCK_STREAM
(TCP). - The socket binds to
127.0.0.1:8080
and listens for connections. - When a client connects, it receives the HTTP request and prints it.

Run the script and open http://127.0.0.1:8080
in a browser. You will see the raw HTTP request printed in the terminal.


Step 2: Handling HTTP Requests
Now, we need to parse the HTTP request to extract the requested resource (e.g., /index.html
) and respond accordingly.
Implementation:
import socket
# Server Configuration
HOST = '127.0.0.1'
PORT = 8080
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Server is running on http://{HOST}:{PORT}")
while True:
client_socket, client_address = server_socket.accept()
request_data = client_socket.recv(1024).decode()
request_lines = request_data.split("\n")
if request_lines:
request_line = request_lines[0] # Example: "GET /index.html HTTP/1.1"
print("Request:", request_line)
# Extract the requested file
request_path = request_line.split(" ")[1]
# Default file if no specific file is requested
if request_path == "/":
request_path = "/index.html"
try:
with open("www" + request_path, "r") as file:
response_body = file.read()
response_header = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n"
except FileNotFoundError:
response_body = "<h1>404 Not Found</h1>"
response_header = "HTTP/1.1 404 Not Found\nContent-Type: text/html\n\n"
response = response_header + response_body
client_socket.sendall(response.encode())
client_socket.close()
Explanation:
- The server extracts the requested file path from the HTTP request.
- If the requested path is
/
, it defaults to/index.html
. - The server attempts to read the requested file from a
www
directory. - If the file exists, it sends an
HTTP 200 OK
response with the file content. - If the file is missing, it sends an
HTTP 404 Not Found
response.
Testing the Server:
- Create a folder named
www
in the same directory as the script. - Inside
www
, create a file namedindex.html
with some sample content:
<!DOCTYPE html>
<html>
<head><title>My Custom HTTP Server</title></head>
<body>
<h1>Welcome to My Custom HTTP Server!</h1>
</body>
</html>
- Start the server and open
http://127.0.0.1:8080
in a browser. - Try visiting
http://127.0.0.1:8080/hello.html
(which doesn’t exist) to trigger a 404 error.

Step 3: Handling Different HTTP Methods
A full-featured web server should handle different HTTP methods like GET
and POST
.
Enhancing the Server to Handle GET and POST Requests:
import socket
HOST = '127.0.0.1'
PORT = 8080
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Server running at http://{HOST}:{PORT}")
while True:
client_socket, client_address = server_socket.accept()
request_data = client_socket.recv(1024).decode()
request_lines = request_data.split("\n")
if request_lines:
request_line = request_lines[0]
method, path, _ = request_line.split(" ")
if method == "GET":
if path == "/":
path = "/index.html"
try:
with open("www" + path, "r") as file:
response_body = file.read()
response_header = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n"
except FileNotFoundError:
response_body = "<h1>404 Not Found</h1>"
response_header = "HTTP/1.1 404 Not Found\nContent-Type: text/html\n\n"
elif method == "POST":
response_body = "<h1>POST Request Received</h1>"
response_header = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n"
else:
response_body = "<h1>405 Method Not Allowed</h1>"
response_header = "HTTP/1.1 405 Method Not Allowed\nContent-Type: text/html\n\n"
response = response_header + response_body
client_socket.sendall(response.encode())
client_socket.close()
Explanation:
- The server checks the HTTP method.
- If it’s
GET
, it serves static files. - If it’s
POST
, it sends a simple response acknowledging the request. - If the method is unsupported, it returns a
405 Method Not Allowed
response.
Testing the POST Request:
Use curl
or a tool like Postman to send a POST
request:
curl -X POST http://127.0.0.1:8080
This will return POST Request Received
.

Step 4: Improving the Server with Multi-threading
A single-threaded server processes one request at a time, making it slow under heavy traffic. Using multi-threading, the server can handle multiple clients simultaneously.
Adding Multi-threading:
import socket
import threading
HOST = '127.0.0.1'
PORT = 8080
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Server running at http://{HOST}:{PORT}")
def handle_client(client_socket):
request_data = client_socket.recv(1024).decode()
request_lines = request_data.split("\n")
if request_lines:
request_line = request_lines[0]
method, path, _ = request_line.split(" ")
response_body = f"<h1>Request Received: {method} {path}</h1>"
response_header = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n"
response = response_header + response_body
client_socket.sendall(response.encode())
client_socket.close()
while True:
client_socket, client_address = server_socket.accept()
thread = threading.Thread(target=handle_client, args=(client_socket,))
thread.start()
This allows the server to process multiple requests in parallel, improving performance.
Conclusion
We built a simple HTTP server from scratch using sockets in Python. It can:
- Handle GET and POST requests.
- Serve static HTML files.
- Process multiple clients simultaneously with threading.