Safeguard
Security Guides•

How do you limit request body size in a Go HTTP handler?

Wrap r.Body with http.MaxBytesReader before you decode it. Go's MaxHeaderBytes limits headers only, so request bodies have no default size cap.

Safeguard Security Team
4 min read

Wrap the request body with http.MaxBytesReader before you read or decode it. Go does not cap request bodies by default, and Server.MaxHeaderBytes limits headers only. MaxBytesReader stops a handler reading past your chosen size, which protects memory and CPU from oversized or malicious uploads.

Definition

http.MaxBytesReader takes the response writer, a reader (normally r.Body) and a byte limit, and returns a reader that fails once the limit is passed. The Go documentation puts its purpose in one sentence: it "prevents clients from accidentally or maliciously sending a large request and wasting server resources."

Header size is a separate control. The source for Server.MaxHeaderBytes says it limits the bytes read parsing header keys and values, including the request line, and that it "does not limit the size of the request body".

The pattern

func create(w http.ResponseWriter, r *http.Request) {
    r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MiB

    var in CreateInput
    dec := json.NewDecoder(r.Body)
    dec.DisallowUnknownFields()
    if err := dec.Decode(&in); err != nil {
        var tooBig *http.MaxBytesError
        if errors.As(err, &tooBig) {
            http.Error(w, "request too large", http.StatusRequestEntityTooLarge)
            return
        }
        http.Error(w, "bad request", http.StatusBadRequest)
        return
    }
    // use in
}

Return 413 for a body that is too large and 400 for malformed input, so clients and logs can tell them apart. Keep the error text generic.

Choosing a limit

Pick the limit per route, not one global number. A login form needs a few kilobytes. A JSON create call may need tens of kilobytes. A file upload may need megabytes and should stream to storage instead of loading into memory.

Route typeTypical approach
Login, tokens, small formsVery small limit, a few KB
JSON API writesLimit from your largest valid payload
File uploadLarger limit, stream to disk or object storage, never read it all into memory
WebhooksLimit to the provider's documented maximum payload

A middleware that applies a default and lets specific routes raise it works well.

Two common mistakes

The first is applying the limit after the body has already been read, for example after calling ParseForm. The limit must wrap the body before any reader touches it. The second is trusting the Content-Length header. A client can declare a small length and send more, or send a chunked body with no length at all, which is why a wrapped reader that counts actual bytes is the dependable control.

Steps

  1. List every handler that reads r.Body, including ParseForm, ParseMultipartForm and JSON decoders.
  2. Add MaxBytesReader at the top of each one, or in middleware.
  3. Decide the limit from real payload sizes and add a margin.
  4. Map the size error to 413 and log it with the route and client, without logging the body.
  5. Add a test that sends a body one byte over the limit.
  6. Pair this with header timeouts. See which timeouts a Go HTTP server needs.

Try it yourself

Click through it at your own pace, or open it full screen. Look at the kinds of API weaknesses a scan reports, including missing input limits.

Quick check

Does MaxBytesReader limit multipart uploads?

It limits whatever you read through the wrapped body. Apply it before ParseMultipartForm, and set a sensible memory limit for that call as well.

Should I rely on the reverse proxy for body limits?

Use it as one layer, not the only one. Traffic that bypasses the proxy, such as internal calls, will not be limited.

Safeguard's dependency scanning and SBOM generation cover the Go modules your service builds from, with reachability analysis to separate reachable findings, and the free scan is at gold.safeguard.sh/scan.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.