How I Built a Web Server in Assembly (From Scratch!)

Table of contents
- π§ Why Assembly?
- β Features of My Web Server
- β Limitations β On Purpose!
- π§Ύ Overview of Server Workflow
- π§΅ Key Snippets (Explained Simply)
- π₯ POST Request Handling (Explained Simply)
- π€ GET Request Handling
- π Memory Buffers Used
- π Key Syscalls Used
- π How to Compile and Run
- π€― The Pain Points
- π‘ What I Learned
- π Final Thoughts

Have you ever wondered what happens under the hood when you type a URL and hit enter? What if you could write a web server from scratch β not in C or Python, but in pure x86_64 Assembly, using only Linux system calls?
Thatβs exactly what I did.
This blog walks you through how I built a minimal HTTP server that can handle GET
and POST
requests β without any standard library, high-level abstractions, or frameworks. Just syscalls, raw sockets, forking, and manual parsing β all in under 500 lines of Assembly.
π§ Why Assembly?
The assembly gives you ultimate control over what the CPU does. I wanted to understand how:
sockets are created and accepted,
HTTP requests are parsed,
file I/O is handled,
processes fork and manage connections.
And thereβs no better way than writing everything manually β byte by byte.
β Features of My Web Server
β Serves files via GET
β Saves data via POST
β Handles concurrent clients with
fork
β No dependencies β not even libc
β Built using Linux syscalls only
β Manual parsing of HTTP/1.0 headers
β Limitations β On Purpose!
Like any handcrafted project built for learning, my assembly-based web server comes with a few intentional limitations. These arenβt flaws β theyβre features that helped me focus on core concepts without overengineering. Here's what you won't find in my server (and why):
No Persistent Connections (Keep-Alive):
Each HTTP request is handled independently. Once the response is sent, the connection closes. Simpler to manage β especially in assembly.Only Supports HTTP/1.0:
I kept it old-school. HTTP/1.0 is easier to implement and perfect for understanding the basics of request-response flow.No MIME Type Detection:
Everything served is treated as plain text, whether it's.html
,.css
, or even.png
. This allowed me to sidestep content negotiation logic.No Request Size Limits or Timeout Handling:
Thereβs no safety net for overly large requests or hanging connections. This tradeoff gave me cleaner code and clearer performance profiling.Not Production-Ready (And Thatβs Okay!):
This server isnβt designed for hosting real-world apps. Itβs built purely for learning how low-level networking, syscalls, and performance tracing really work.
π§Ύ Overview of Server Workflow
_start
βββ socket β bind β listen
βββ Loop:
βββ accept
βββ fork
βββ In child:
β βββ read request
β βββ parse GET/POST
β βββ handle file I/O
β βββ send HTTP response
βββ In parent:
βββ close client socket
π§΅ Key Snippets (Explained Simply)
1. Accept + Fork for Concurrent Clients
mov rax, 43 ; syscall number for accept
syscall
mov r12, rax ; save new socket FD
mov rax, 57 ; syscall number for fork
syscall
2. Parse HTTP Method and Path
lodsb ; load byte from request
cmp al, ' ' ; stop at space
je done_copying
3. Write HTTP Response
mov rdi, 4 ; client socket FD
lea rsi, [msg] ; HTTP/1.0 200 OK header
mov rdx, 19 ; length of message
mov rax, 1 ; syscall: write
syscall
π₯ POST Request Handling (Explained Simply)
The server reads the
POST /file.txt HTTP/1.0
line.It extracts the file path (
file.txt
).It reads until the HTTP headers end (
\r\n\r\n
).The remaining body is saved to
file.txt
.Returns a simple
200 OK
.
π€ GET Request Handling
Extracts the path from
GET /file.txt HTTP/1.0
.Opens the file and reads its contents.
Sends back the data with a
200 OK
header.
π Memory Buffers Used
Buffer | Purpose |
buffer | Stores incoming request |
open_path | Stores the path from the URL |
read_file | Stores file contents or the POST body |
bytes_read | Tracks how many bytes were read |
socket_addr | Struct for bind and accept |
π Key Syscalls Used
socket()
: Create the server socket.bind()
: Bind it to port 80.listen()
: Start listening.accept()
: Accept a new client connection.fork()
: Spawn a new child to handle it.read()
: Read HTTP request.open()
,read()
(again): Load file (GET) or parse body (POST).write()
: Send HTTP response.close()
: Close descriptors.
Each of these had to be done manually, including setting up syscall numbers, managing memory and buffers, and handling string comparisons in assembly.
π How to Compile and Run
Step 1: Assemble and Link
nasm -f elf64 server.asm -o server.o
ld server.o -o server
Step 2: Run with Root Privileges (port 80)
sudo ./server
Now try:
curl http://localhost/file.txt # GET
curl -X POST -d "Hello" /file.txt # POST
π€― The Pain Points
Parsing HTTP in assembly? Not fun. I had to scan for newline characters, spaces, and manually extract the path.
Memory management: Thereβs no malloc here β you predefine buffers or use syscalls like
mmap
.Concurrency: Forking worked, but it took some tweaking to make sure sockets were closed properly.
Debugging: GDB was my best friend. I stared at syscall return codes like my life depended on it.
π‘ What I Learned
This wasnβt just a coding challenge. It was a deep dive into:
How Linux syscalls really work
File descriptors and sockets at a low level
Parsing raw text (like HTTP) in memory
The elegance (and brutality) of assembly
It gave me a new appreciation for the layers of abstraction modern languages offer β and what lies underneath.
π Final Thoughts
Building a web server in assembly might sound unhinged β and honestly, it kind of is. But thatβs exactly what makes it powerful. It strips away abstractions and forces you to face how the web and operating systems actually work at the lowest level.
If you're diving into cybersecurity β especially exploit development or red teaming β this kind of project is gold. It teaches you to see what most people miss: what really happens under the hood when packets move, syscalls fire, and memory is allocated.
This isnβt just about writing a web server. Itβs about thinking like a hacker.
The full technical breakdown is up on my GitHub writeup. Would love to hear your thoughts or improvements β letβs geek out!
Subscribe to my newsletter
Read articles from Ashbal Fatima directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ashbal Fatima
Ashbal Fatima
Penetration Testing enthusiast with hands-on experience in offensive security and red teaming. Skilled in web, network, and Linux security using Hack The Box, pwn.college, and PortSwigger. Focused on practical exploitation, vulnerability analysis, and building a strong offensive toolkit. Currently expanding into cloud, AWS, and WiFi pentesting.