You Use HTTP Every Day. I Tried Rebuilding It.

RohitRohit
5 min read

Rebuilding a Proxy Server from Scratch to Understand the Web

I believe that to become a better developer, you need to understand the systems you rely on every day, things like operating systems, databases, web servers, and frameworks. And the best way to understand them isn't by reading about them, it's by rebuilding them. One piece at a time.

Why I Even Bothered Writing a Proxy Server in C

Since I spend most of my time writing APIs and building frontend code that consumes them, it honestly started to bug me—how does the data really get from one point to another?

Sending HTTP requests is something we do every single day as web developers. But I realized I never truly understood what happens behind the scenes. That curiosity led me to build a proxy server—not just to forward requests, but to do it the hard way: handling multiple connections using multi-threading, caching responses with an LRU cache, and learning how data flows across sockets, how synchronization works under load, and how real systems hold up when traffic gets heavy.

I didn’t build this because I needed a proxy server. I built it because I use the internet every day and I wanted to stop treating it like a black box, and this felt like a good excuse to dig into sockets, multi-threading, and caching all at once.

So What Is a Proxy Server, Really?

A proxy server sits between your browser and the destination server. When you type in a URL, instead of sending the request directly to the site, your browser sends it to the proxy. The proxy then forwards it to the site, grabs the response, and gives it back to you.

It can also cache things, so if someone else asks for the same page, the proxy doesn’t need to fetch it again. This reduces network load and makes things faster.

What My Proxy Server Does

Here’s what mine actually does, stripped down to the basics:

  • Listens for incoming HTTP GET requests

  • Parses the request to figure out where it's headed

  • Spawns a new thread to handle each client

  • Uses mutexes and semaphores to manage concurrent access to shared resources

  • Checks if a response is already cached

    • If it is, returns the cached data

    • If it isn’t, forwards the request, gets the response, caches it (if size allows), and returns it

That’s it. No HTTPS. No fancy routing. Just the bare bones of how a proxy works.

The Threading Headaches

Writing multi-threaded code in C is a trip. Things work... until they don’t. I hit issues like race conditions where two threads accessed the same part of memory at the same time. I had to lock everything properly using mutexes, and I added semaphores to limit the number of concurrent threads.

These bugs were hard to spot. They don’t always crash your app—they just make it behave weirdly. That’s the worst kind of bug.

What I Learned

This project made me realize just how much I was taking for granted.

Before this, I had no idea how hard it was to build even a simple cache, or how much thought goes into managing threads safely. Things like freeing memory at the right time or keeping your sockets clean aren’t just details—they’re everything.

I learned how to:

  • Open sockets manually and read/write raw data

  • Parse HTTP requests without any libraries

  • Manage memory for dynamic buffers

  • Build a simple but working LRU cache

  • Write thread-safe code in C

Most importantly, I learned that systems break in subtle, quiet ways, and that debugging those issues teaches you more than any tutorial ever could.

Things That Broke

I had requests hanging because I wasn’t closing sockets properly. The cache would sometimes return garbage data until I figured out I needed to lock every access to it. I made a dozen mistakes just parsing basic HTTP requests.

Threads crashed. Memory leaked. But that was the point. Every time something broke, I understood the system a little better.

Why It Was Worth It

I won’t pretend this is production-grade code. It’s not. It’s a learning project.

But doing this changed how I think. I understand the flow of data from the browser to the server and back. I can visualize what’s happening at the socket level. And now, when I build higher-level APIs or frontends, I know what’s going on underneath the hood.

You don’t need to build a proxy server to be a better developer, but I do think everyone should try building something low-level at least once. Maybe a shell. Maybe a basic web server. Maybe just a TCP client.

Because once you build the building blocks yourself everything you do on top of them starts to make way more sense.

What’s Next

There’s still stuff I want to add. HTTPS would be cool, but I’d need to dive into TLS. Better logging, support for more HTTP methods, maybe even chunked responses. Who knows, maybe I’ll turn it into a reverse proxy one day.

But for now, I’m just glad I did it.

Final Thoughts

This project wasn’t about writing perfect code or building something flashy. It was about learning, getting stuck, and figuring things out along the way.

Yeah, things broke. I messed up threading more than once. But each bug taught me something. Each weird crash helped me understand how systems actually behave when they’re under stress.

And by the end of it, I came out more confident—not just in C, but in how data moves across the web, how to handle concurrency, and how to think about code that has to run for real.

If you’re someone who wants to actually understand what’s going on behind the scenes, I can’t recommend this enough. Pick a small piece of the stack and rebuild it from scratch. Not for a portfolio. Not for a resume. Just for yourself.

This proxy server gave me a deeper understanding than I expected. And I’m honestly excited to keep going and see what else I can build from the ground up.

0
Subscribe to my newsletter

Read articles from Rohit directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rohit
Rohit