Safeguard
Security Guides•

Does Go's http.Client have a timeout by default?

No. Go's http.Client Timeout of zero means no timeout, and DefaultClient uses it. Here is how to set client, request and context deadlines safely.

Safeguard Security Team
4 min read

No. The Timeout field on Go's http.Client is zero by default, and the documentation says "a Timeout of zero means no timeout". http.Get and http.DefaultClient use that zero value, so a call to a slow or hostile server can wait forever. Set a Client Timeout or pass a context with a deadline.

What the field covers

The Go source describes Client.Timeout as the limit for a request made by the client. It "includes connection time, any redirects, and reading the response body". It also says the timer keeps running after Get, Head, Post or Do return, and will interrupt reading of Response.Body.

That last point matters. A common bug is to think the timeout ends when headers arrive. It does not, so a server that sends headers and then trickles the body will be cut off by the total deadline, not left hanging.

Why this is a security issue and not only a reliability one

An outbound call without a deadline holds a goroutine, a connection and often a slot in a worker pool. If an attacker can make your service call a URL they control, for example through a webhook or an image fetch feature, they can stall your workers on purpose. That turns a missing timeout into a denial of service lever.

Three ways to bound a call

ApproachScopeGood for
Client.TimeoutEvery request from that client, totalA safe default on every client you build
Context with deadlineOne requestPer call budgets that follow a parent deadline
Transport settingsDial, TLS handshake, response header phasesFine grained limits on connection stages

Context deadlines work well with the rest of your call chain. The context package documents that WithTimeout returns WithDeadline with the current time plus the timeout, and that "failing to call the CancelFunc leaks the child and its children until the parent is canceled". Always call cancel, usually with defer.

ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
    return err
}
resp, err := client.Do(req)

Steps

  1. Search your code for http.Get, http.Post and http.DefaultClient. Each uses the zero timeout.
  2. Build one shared client with a Timeout, and reuse it. Do not create a client per request.
  3. Pass the incoming request context into outbound calls so a cancelled user request stops downstream work.
  4. For calls to user supplied URLs, add limits on response size and redirect count as well as time.
  5. Close response bodies, and read only what you need.

Try it yourself

Click through it at your own pace, or open it full screen. Watch how a code scan points at the exact line, which is how a stray http.Get gets found.

Quick check

Is a context deadline the same as Client.Timeout?

They overlap. Client.Timeout applies to every request from one client. A context deadline applies to one request or call chain. If both are set, whichever expires first ends the request.

Does a timeout stop a very large response?

Only by time. Cap the size too, for example by wrapping the body in a limited reader.

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.