back to blogs

November 17, 2025 (3d ago)2 min read

Let's understand Go Handle(r) family - last thing you need

Go Handler Family Diagram

Go Handler Family Diagram

When you begin writing HTTP servers in Go, the words Handler, HandlerFunc, Handle, and HandleFunc look confusing. They sound similar, but each plays a clear role. Once you see how they connect, everything becomes simple.

Go's HTTP system starts with the Handler interface. It has just one method:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

Any type that has this method becomes a Handler. For example, a struct can turn into a Handler like this:

type Hello struct {}

func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello from struct Handler"))
}

Now this struct can be registered in the router.

The next piece is HandlerFunc. This is not an interface. It is a function type:

type HandlerFunc func(ResponseWriter, *Request)

Go attaches a method to this function type:

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

This means any ordinary function with that signature automatically behaves like a Handler. Example:

func hello(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello"))
}

Because this function matches HandlerFunc, you can register it like this:

http.Handle("/hello", http.HandlerFunc(hello))

Or using the shorter helper:

http.HandleFunc("/hello", hello)

This is where newcomers get confused. The difference is simple:

Handle expects something that already implements the Handler interface.

HandleFunc expects a plain function, and Go converts it into a HandlerFunc automatically, which already has ServeHTTP.

So if you write a struct with ServeHTTP, use Handle:

http.Handle("/hi", Hello{})

If you write a simple function, use HandleFunc:

http.HandleFunc("/hi", hello)

Both eventually produce a Handler. The only difference is whether Go wraps it for you or you pass a Handler directly.

This entire design keeps Go's HTTP package clean and flexible. You can use functions, closures, or full struct-based handlers. Everything works because HandlerFunc turns a normal function into a real Handler by giving it the ServeHTTP method.

Once you understand that, the whole Handler family feels natural: Handler is the interface, HandlerFunc is a function type with ServeHTTP, and Handle/HandleFunc simply register your routes.

crafted by: atharva

last visitor:

Pune, +IST
© 2025 Atharva Mhaske.