Most Go developers reach for net/http without a second thought. It's the standard library, it works, it's fast. But recently I asked myself a question: what actually happens between the TCP connection and the HTTP handler?
So I built httpfromtcp — a complete HTTP/1.1 server built on raw TCP sockets, with zero dependency on net/http.
Here's what I learned.
The TCP Layer
When a client connects to your server, what arrives is just bytes — a stream of them. HTTP is a text protocol layered on top. The raw request looks like this:
GET /hello HTTP/1.1\r\n
Host: localhost:8080\r\n
Accept: /\r\n
\r\n
Your job, as the server, is to read those bytes, parse the method, path, headers, and body — then write back a valid HTTP response.
In Go, this is net.Listener + conn.Read() + conn.Write(). No magic.
Parsing HTTP by Hand
The first major hurdle is the request parser. HTTP/1.1 uses \r\n (CRLF) as line endings. The request line is always METHOD /path HTTP/VERSION. Headers follow until you hit a blank line, then the body starts.
// Simplified
func parseRequest(conn net.Conn) (Request, error) {
reader := bufio.NewReader(conn)
requestLine, _ := reader.ReadString('\n')
parts := strings.Fields(requestLine)
method, path := parts[0], parts[1]
// ... parse headers
}
This feels obvious in retrospect, but building it from scratch makes every future http.Request feel less like magic.
Route Matching
Route matching is just string comparison with some path parameter extraction. I ended up with a simple pattern like "GET /users/:id" where :id becomes a named capture.
server.Handle("GET /users/:id", func(req Req, rw *ResponseWriter) {
id := req.PathParam("id")
// ...
})
No regex. No trie. A simple linear scan over registered routes is fast enough for any local tool or internal service.
What This Taught Me
1. HTTP is simpler than you think — the complexity is in edge cases (chunked encoding, keep-alive, TLS), not the core protocol.
2. net/http is doing a lot of work for you — and now I appreciate it more, not less.
3. Reading specs is a superpower — RFC 9112 is surprisingly readable.
4. Building things from scratch is the best way to learn — not just about protocols, but about Go itself.
httpfromtcp is open source. Check it out on GitHub →
