Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsIn Go, HTTP middleware is a handler that wraps another handler to add shared behavior before or after it runs. Because both the wrapper and the handler it calls implement http.Handler, you can combine layers without changing the endpoint itself.
What Go HTTP middleware does
The net/http package defines a server-side handler through the http.Handler interface. Its ServeHTTP(http.ResponseWriter, *http.Request) method handles a request and writes a response. Middleware follows a common pattern: it accepts one handler and returns another, usually with the signature func(http.Handler) http.Handler. The returned handler can inspect or change the request, add response headers, enforce a check, or perform work around the call to the original handler. Go net/http API documentation
As an Amazon Associate I earn from qualifying purchases.
Go does not automatically provide logging, authentication, request IDs, or other middleware behavior. Those are examples of features you can implement in a wrapper or obtain from a separate library.
How a request moves through middleware
Each wrapper receives the request first, then calls the next handler with next.ServeHTTP(w, r). Work before that call runs on the way in; work after it runs when the inner handler returns. For outer(inner(endpoint)), the order is:
#1 Best Overall
outerpre-handler workinnerpre-handler work- The endpoint
innerpost-handler workouterpost-handler work
This nesting order matters for tasks such as recording elapsed time, adding headers, or checking authorization. If a helper builds the stack by looping over a middleware slice, the loop direction determines which item becomes outermost; make that convention explicit.
Build a small middleware wrapper
This example measures how long a handler takes and logs its duration. The middleware uses the standard library only:
func timing(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
started := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s took %s", r.Method, r.URL.Path, time.Since(started))
})
}
http.HandlerFunc adapts a function with the ServeHTTP signature into an http.Handler, making it convenient to define middleware inline. Go net/http API documentation
To apply the wrapper directly to an endpoint:
handler := timing(http.HandlerFunc(endpoint))
To apply it around a mux, construct the mux explicitly so the composition is visible:
mux := http.NewServeMux()
mux.HandleFunc("GET /hello", hello)
handler := timing(mux)
server := &http.Server{
Addr: ":8080",
Handler: handler,
}
log.Fatal(server.ListenAndServe())
The method-qualified route pattern shown here uses the ServeMux syntax introduced in Go 1.22. That release changed mux pattern matching; it did not change the middleware pattern. The GODEBUG=httpmuxgo121=1 setting restores prior mux behavior and is read when the process starts. Check the net/http documentation for the version-specific routing rules.
Compose multiple middleware layers
Since each middleware accepts and returns an http.Handler, layers can be nested. Here, the request ID wrapper is outermost, so it runs before authentication and can make the ID available to inner layers:
handler := requestID(authenticate(mux))
For a reusable helper, choose and document whether the first middleware in a slice is outermost. This implementation makes the first item outermost:
func chain(handler http.Handler, middleware ...func(http.Handler) http.Handler) http.Handler {
for i := len(middleware) - 1; i >= 0; i-- {
handler = middleware[i](handler)
}
return handler
}
handler := chain(mux, requestID, authenticate, timing)
With this convention, request processing enters requestID, then authenticate, then timing, before reaching the mux. Return processing unwinds in reverse order.
Choose standard-library composition or a router
For a small service, the standard-library approach provides explicit handler composition without adding a dependency. A third-party router or framework may offer its own middleware registration API, but features such as per-route registration and ordering vary by library. Evaluate the specific router’s documentation and how its wrappers preserve ResponseWriter behavior rather than assuming every framework works the same way.
Rank #4
Handler details that can cause bugs
Write headers before the response
Set response headers before the handler commits the response by writing a status or body. Middleware that needs to observe status codes or bytes often wraps http.ResponseWriter; such a wrapper may also need to preserve optional interfaces or capabilities that downstream handlers expect. There is no universal wrapper implementation that safely covers every capability, so account for the interfaces used by your application. Go net/http API documentation
Use the writer only while the handler runs
A handler should write its reply and return. The ResponseWriter must not be used after ServeHTTP returns. That rules out asynchronously writing a response from a goroutine launched by middleware after the wrapped handler finishes. Go net/http API documentation
Free tools Windows power users keep installed
One-click scans. No signup required.
Read request bodies with care
Handlers should generally treat the provided request as read-only, apart from reading its body. The API documentation cautions that whether a request body can still be read after writing a response may depend on the client, protocol version, and intermediaries. If your middleware or endpoint needs the body, read it before writing the response. Go net/http API documentation
Best Value
Recovery is not error handling
Recovery middleware can contain a panic in a handler and help keep a server process available, but it does not replace returning or handling ordinary errors correctly. The standard library documents server behavior when a handler panics; use that contract when deciding what recovery should do. Go net/http API documentation
Response controls and routing are separate concerns
Most middleware only needs the ordinary handler contract. For advanced per-request response operations, Go 1.20 added http.ResponseController, including support for read and write deadlines. It is relevant to response-control or streaming use cases, not a prerequisite for basic middleware. Go 1.20 release notes
Likewise, ServeMux pattern syntax determines which handler receives a request; it is distinct from the mechanism that wraps a handler. The net/http package documentation explains that a server accepts a handler, and that passing nil to ListenAndServe uses the default mux. Explicit muxes make middleware composition easier to see and test.
Recommended Free Tools
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




