Why does a middleware function specify http.Handler as its return type, yet return an http.HandlerFunc instead?
Great question — this is a common point of confusion in Go's HTTP design, and is one of those things that feels a bit magical until you see the type relationships clearly.
Let's break it down simply.
You often see middleware functions in Go like this:
func JWTAuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// middleware logic
next.ServeHTTP(w, r)
})
}
At first glance, it looks odd — the function returns http.Handler, but the return statement gives http.HandlerFunc instead.
How does that manage to compile?
The key concept here is that http.HandlerFunc implements http.Handler. Let's look at the interface definition from Go's standard library:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
And here is the type definition for http.HandlerFunc:
type HandlerFunc func(ResponseWriter, *Request)
Notice — it's just a function type. But Go adds this method to it:
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
So a value of type http.HandlerFunc has a method named ServeHTTP. That means it implements the http.Handler interface automatically.
Therefore, when you write:
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// middleware logic
next.ServeHTTP(w, r)
})
you are returning a function value of type http.HandlerFunc.
Since http.HandlerFunc implements the http.Handler interface, Go automatically treats it as a valid http.Handler.
So it's 100% correct and idiomatic.