Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk4 min

Go HTTP Middleware: How It Works and How to Build Your Own

Go HTTP middleware wraps an http.Handler to add behavior before or after an endpoint. See a working example, composition order, and key handler cautions.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

In 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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. outer pre-handler work
  2. inner pre-handler work
  3. The endpoint
  4. inner post-handler work
  5. outer post-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

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.