Networking#

Networking is one of Go’s strengths. The standard library covers HTTP, TLS, TCP/UDP, and Unix sockets out of the box.

HTTP Client#

The net/http package ships a default client that’s fine for one-offs but lacks timeouts, a foot-gun in production. For anything that’s not throwaway, build a http.Client with explicit timeouts and a tuned Transport so connection pooling, idle timeouts, and HTTP/2 behave predictably.

import "net/http"

resp, err := http.Get("https://example.com/")
if err != nil { return err }
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)

For anything beyond a one-off, use a custom client with timeouts.

client := &http.Client{
    Timeout: 10 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
        ForceAttemptHTTP2:   true,
    },
}
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, err := client.Do(req)

HTTP Server#

The same package serves HTTP. http.NewServeMux is the stdlib router; explicit timeouts on http.Server are required in production; the zero value never times out. Many teams reach for chi, gin, or echo for richer routing, but the stdlib is sufficient for most services.

mux := http.NewServeMux()
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
})

srv := &http.Server{
    Addr:              ":8080",
    Handler:           mux,
    ReadHeaderTimeout: 5 * time.Second,
    ReadTimeout:       15 * time.Second,
    WriteTimeout:      30 * time.Second,
    IdleTimeout:       120 * time.Second,
}
log.Fatal(srv.ListenAndServe())

The Go 1.22 mux supports method-aware patterns: mux.HandleFunc("GET /users/{id}", handler).

TCP and UDP#

The net package wraps BSD sockets with a Go-idiomatic interface. net.Listen accepts; net.DialTimeout opens client connections with a deadline; net.ResolveUDPAddr and net.DialUDP cover datagrams. Each goroutine handling a connection is the standard concurrency pattern.

// TCP server
ln, err := net.Listen("tcp", ":9000")
for {
    conn, err := ln.Accept()
    go handle(conn)
}

// TCP client
conn, err := net.DialTimeout("tcp", "host:9000", 5*time.Second)

// UDP
addr, _ := net.ResolveUDPAddr("udp", "host:53")
conn, _ := net.DialUDP("udp", nil, addr)

TLS#

The crypto/tls package wraps OpenSSL-compatible TLS in pure Go. tls.Config{MinVersion: VersionTLS12} is the modern minimum; never set InsecureSkipVerify in production. ListenAndServeTLS is the server side; autocert automates Let’s Encrypt certificate issuance.

import "crypto/tls"

client := &http.Client{
    Transport: &http.Transport{
        TLSClientConfig: &tls.Config{
            MinVersion: tls.VersionTLS12,
            // Never set InsecureSkipVerify in production.
        },
    },
}

// TLS server
srv.ListenAndServeTLS("cert.pem", "key.pem")

Use golang.org/x/crypto/acme/autocert for automatic Let’s Encrypt certs.

Context and Cancellation#

Pass context.Context through every network call so callers can cancel:

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

req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := client.Do(req)

gRPC#

Go has first-class gRPC support through google.golang.org/grpc. Generated stubs from protoc-gen-go-grpc provide typed client and server interfaces; TLS credentials, deadlines, and interceptors plug into the same connection. Streaming RPCs work as goroutine-friendly channel-shaped APIs.

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
)

creds := credentials.NewClientTLSFromCert(nil, "")
conn, err := grpc.NewClient("host:443", grpc.WithTransportCredentials(creds))

WebSockets#

The standard library doesn’t ship a WebSocket implementation; use.

DNS#

  • net.LookupHost / net.LookupAddr / net.LookupTXT for the system resolver.

  • net.Resolver{PreferGo: true} to use the pure-Go resolver.

  • miekg/dns for serving or making custom DNS queries.

HTTP Routing Frameworks#

See Frameworks for higher-level options (chi, gin, echo, fiber). For most services, the standard net/http plus chi is more than enough.

Pitfalls#

The traps that catch Go networking authors. Default clients have no timeouts; connection leaks happen when bodies aren’t closed; HTTP/1.1 connections only reuse if drained; http.DefaultClient should never be used in production; load-balancer idle timeouts can produce surprising resets.

  • No default timeouts on http.Client, a hung server hangs your program forever.

  • Connection leaks, always defer resp.Body.Close(), even on error responses.

  • Reading the body matters, HTTP/1.1 connections only get reused if the body is fully drained.

  • ``http.DefaultClient`` has no timeout. Build your own.

  • Keep-alive, on by default; that’s usually what you want, but watch for “connection reset” if a load balancer idle-closes.