What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Go’s net/http package handles both sides of HTTP: use an http.Client to send requests, and implement an http.Handler to receive them. For a quick GET, http.Get is convenient; for production requests, build a request with a context, reuse a configured client, check the status code, and close the response body. On the server side, register handlers with a mux and configure an http.Server with timeouts appropriate to your application.
This guide covers the practical client and server workflows, connection reuse, contexts, redirects, testing, and common failure modes. Examples use the standard library; check the net/http documentation for the Go version your project supports.
What net/http provides
The Go standard library’s net/http package provides HTTP client and server implementations. Its two sides meet at request and response types: clients construct an http.Request and receive an http.Response; servers pass a request to a handler, which writes through an http.ResponseWriter. See the package documentation and its package-level examples.
For a basic outgoing request, choose http.Get. When you need a method other than GET, custom headers or a body, cancellation, or specific redirect and transport behavior, create a request and send it through a reusable client. For incoming traffic, implement a handler, register it with a mux, and serve it.
#1 Best Overall
Make an HTTP request with net/http
Use http.Get for a simple GET
http.Get(url) is a concise option when the default client behavior is suitable. A successful call means the request completed at the HTTP level, not that the server returned an application-level success status. A 404 or 500 response, for example, is still a response: inspect StatusCode yourself.
Build a request when you need control
This example uses a five-second context deadline, checks for a 2xx response, and limits how many response bytes it reads. Replace the example endpoint with one appropriate to your application.
package main
import (
"context"
"fmt"
"io"
"net/http"
"time"
)
func fetch(ctx context.Context, client *http.Client, endpoint string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("send request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("unexpected HTTP status: %s", resp.Status)
}
// Read at most 1 MiB plus one byte so an oversized response is detectable.
const maxBody = 1 << 20
body, err := io.ReadAll(io.LimitReader(resp.Body, maxBody+1))
if err != nil {
return nil, fmt.Errorf("read response: %w", err)
}
if len(body) > maxBody {
return nil, fmt.Errorf("response exceeds %d bytes", maxBody)
}
return body, nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client := &http.Client{}
body, err := fetch(ctx, client, "https://example.com/")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Received %d bytesn", len(body))
}
The example’s size cap is an application choice, not a universal limit. Choose a bound appropriate to the response you expect; decode structured data only after deciding how to handle oversized, malformed, or unsuccessful responses.
Request bodies and headers
For a POST or another method with a body, pass an io.Reader to http.NewRequestWithContext. Set headers on the resulting request before calling Do. For example, JSON requests commonly set Content-Type to application/json; that header describes the body format and does not itself encode or validate the body. Check the endpoint’s contract for required headers, authentication, and response format.
Callers own the response body and should close it when finished, including when they reject a status or encounter a decoding error. The body is streamed, and leaving it open can interfere with reuse of persistent connections. When reading an error response, close its body too.
Choose a client and transport
Reuse clients for the same policy
Use a reusable http.Client rather than constructing one for each request. Clients and transports are safe for concurrent use, and transports cache connections for reuse. A client represents higher-level policy such as redirects and cookies; its transport controls lower-level network behavior.
Use the default client when its behavior is suitable. Configure a client when the application needs a different timeout or redirect policy. A request context is usually the clearer way to bound an individual operation, because its deadline travels with that request.
Configure transport behavior deliberately
A custom http.Transport is where options for proxies, TLS, keep-alives, compression, and connection management belong. Settings such as MaxIdleConns, MaxIdleConnsPerHost, and IdleConnTimeout control idle connection reuse and resource use. Call CloseIdleConnections when the application has a reason to release idle connections.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Do not create a new transport for every request: doing so discards the connection reuse that a shared transport makes possible. The default transport supports HTTP/2 in the documented HTTPS configuration. A custom transport does not enable HTTP/2 by default; if protocol behavior matters, consult the documentation for the Go version you target and configure it explicitly as needed.
Use contexts, deadlines, and redirects safely
Cancel work that outlives its caller
A request context controls the outgoing request lifecycle, including connection acquisition, transmission, and reading response headers and body. Create a deadline or cancellation path tied to the work that initiated the request, then call the returned cancel function to release associated resources. Without a deadline or cancellation mechanism, an outbound operation may continue beyond the period in which its result is useful.
Incoming server request contexts are canceled when the client connection closes, the request is canceled under HTTP/2, or the handler returns. Pass that context to downstream work so it can stop when the request is no longer being handled.
Make redirect and credential decisions explicit
The client follows redirects according to its redirect policy. Go’s security guidance describes stripping sensitive headers on cross-domain redirects as defense in depth against sending credentials to an untrusted destination. That safeguard is not a substitute for deciding which destinations your application trusts. If requests carry sensitive headers, review redirect behavior and apply a policy appropriate to the service and data involved. See Go Security Decisions.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #4
Write and serve an HTTP handler
Implement a handler and register a route
A handler receives a ResponseWriter and a Request. The mux selects which handler receives each request. This runnable minimal server writes a plain-text response:
package main
import (
"log"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/hello" {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
_, _ = w.Write([]byte("Hello, Go HTTP!n"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", hello)
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
log.Fatal(server.ListenAndServe())
}
Run it with go run ., then request http://localhost:8080/hello. The call to ListenAndServe normally returns when it encounters an error, so handle that return value rather than silently discarding it. Go’s introductory Writing Web Applications guide also demonstrates registering a handler and listening on port 8080.
Use an explicit server for deployed applications
An explicit http.Server makes server controls visible where the application configures its listener:
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: maxHeaderBytes,
}
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
Define readTimeout, writeTimeout, and maxHeaderBytes from values selected for your workload and deployment. There is no single correct timeout for every application. A handler serving long-lived responses, for example, has different needs from one serving short API calls.
Best Value
Validate host and input; escape output
Treat request data as untrusted. If inserting a URL path or other user-controlled value into HTML, escape it rather than concatenating it into markup; the official web-application tutorial demonstrates html.EscapeString. Validate routes and any assumptions about the request host. The Request.Host documentation advises handlers to ensure the host is one for which the application considers itself authoritative; host-specific mux patterns can help constrain registered handlers.
Test handlers without a live external service
The net/http/httptest package provides utilities for testing handlers and running test servers. A handler test can build a request intended for a server handler with httptest.NewRequest, send it through a recorder, then inspect the response code, headers, and body. Consult the current httptest package documentation for the helpers available in your Go version.
func TestHello(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/hello", nil)
rec := httptest.NewRecorder()
hello(rec, req)
res := rec.Result()
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Fatalf("status = %d, want %d", res.StatusCode, http.StatusOK)
}
}
Import net/http, net/http/httptest, and testing in the test file. Testing a handler directly keeps the test independent of a live network listener; use the package’s test-server utilities when the behavior under test requires a real client/server interaction.
Troubleshoot common net/http problems
- The call succeeds but the application treats the result as an error: check
resp.StatusCode. A non-2xx status does not, by itself, makeClient.Doreturn an error. - Connections are not being reused or resources accumulate: close every response body, including bodies from status codes you reject. Reuse a client and transport instead of creating a transport for each request.
- A request hangs longer than useful: attach a request context with cancellation or a deadline, and pass server request contexts into downstream operations.
- A request unexpectedly reaches another URL: inspect redirect policy, especially when credentials or other sensitive headers are involved. Do not rely on redirect defaults as your application’s trust policy.
- A custom transport behaves differently from the default: custom transports do not enable HTTP/2 by default. Check protocol configuration for the Go version you target.
- The server stalls on slow or excessive traffic: review server read and write timeout settings and header-size limits for your deployment. Select values for the workload rather than copying a universal number.
- A handler reflects unsafe markup or accepts an unexpected hostname: escape untrusted values before inserting them into HTML and validate host expectations explicitly.
- The server exits immediately: inspect and report the error returned by
ListenAndServe; a listening call normally returns only when there is an error.
Or skip the browser setup
If your Go workflow needs a website screenshot rather than a general-purpose HTTP response, ScreenshotNeo provides a screenshot API. Here is its one-call cURL example; for a Go client, the same request can be made with net/http by setting the query parameters and saving the response body. See the ScreenshotNeo API docs.
Recommended Free Tools
Quick Recap
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server offers take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. See ScreenshotNeo for product details, then sign up for the free plan.
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.




