Skip to main content
Package http provides HTTP client and server implementations. Get, Head, Post, and PostForm make HTTP requests. The Client type supports custom configurations. The Server type provides an HTTP server.

HTTP Client

Making Requests

Get
func Get(url string) (resp *Response, err error)
Issues a GET request to the specified URL. Returns the response and any error encountered.
resp, err := http.Get("https://api.example.com/data")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
Post
func Post(url, contentType string, body io.Reader) (resp *Response, err error)
Issues a POST request to the specified URL.
data := strings.NewReader(`{"name":"John"}`)
resp, err := http.Post("https://api.example.com/users",
    "application/json", data)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()
PostForm
func PostForm(url string, data url.Values) (resp *Response, err error)
Issues a POST request with form data.
data := url.Values{}
data.Set("username", "john")
data.Set("password", "secret")

resp, err := http.PostForm("https://example.com/login", data)
Head
func Head(url string) (resp *Response, err error)
Issues a HEAD request to the specified URL.
resp, err := http.Head("https://example.com")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Status:", resp.Status)
fmt.Println("Content-Length:", resp.ContentLength)

Client Type

Client
type
An HTTP client. The zero value (DefaultClient) is a usable client that uses DefaultTransport. Clients are safe for concurrent use by multiple goroutines.
type Client struct {
    Transport     RoundTripper
    CheckRedirect func(req *Request, via []*Request) error
    Jar           CookieJar
    Timeout       time.Duration
}
Custom client with timeout:
client := &http.Client{
    Timeout: 10 * time.Second,
}

resp, err := client.Get("https://api.example.com")
Client.Do
func (c *Client) Do(req *Request) (*Response, error)
Sends an HTTP request and returns an HTTP response.
req, _ := http.NewRequest("GET", "https://api.example.com", nil)
req.Header.Add("Authorization", "Bearer token123")

client := &http.Client{}
resp, err := client.Do(req)

Request Type

NewRequest
func NewRequest(method, url string, body io.Reader) (*Request, error)
Returns a new Request given a method, URL, and optional body.
// GET request
req, err := http.NewRequest("GET", "https://api.example.com/users", nil)

// POST request with body
data := strings.NewReader(`{"name":"Alice"}`)
req, err := http.NewRequest("POST", "https://api.example.com/users", data)
req.Header.Set("Content-Type", "application/json")
NewRequestWithContext
func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error)
Like NewRequest but includes a context for cancellation and timeouts.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
Request
type
Represents an HTTP request received by a server or to be sent by a client.
type Request struct {
    Method string
    URL    *url.URL
    Header Header
    Body   io.ReadCloser
    // ... more fields
}

Response Type

Response
type
Represents the response from an HTTP request.
type Response struct {
    Status     string // e.g. "200 OK"
    StatusCode int    // e.g. 200
    Header     Header
    Body       io.ReadCloser
    // ... more fields
}
Always close the response body:
resp, err := http.Get(url)
if err != nil {
    return err
}
defer resp.Body.Close()

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

HTTP Server

Creating a Server

ListenAndServe
func ListenAndServe(addr string, handler Handler) error
Listens on the TCP network address and serves HTTP requests.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
})

log.Fatal(http.ListenAndServe(":8080", nil))
ListenAndServeTLS
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error
Like ListenAndServe but for HTTPS.
log.Fatal(http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil))
Server
type
Defines parameters for running an HTTP server.
type Server struct {
    Addr         string
    Handler      Handler
    ReadTimeout  time.Duration
    WriteTimeout time.Duration
    // ... more fields
}
Custom server:
server := &http.Server{
    Addr:           ":8080",
    ReadTimeout:    10 * time.Second,
    WriteTimeout:   10 * time.Second,
    MaxHeaderBytes: 1 << 20,
}

log.Fatal(server.ListenAndServe())

Handler Interface

Handler
interface
Objects that can serve HTTP requests.
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
Custom handler:
type MyHandler struct{}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello from custom handler"))
}

http.ListenAndServe(":8080", &MyHandler{})
HandlerFunc
type
An adapter to allow use of ordinary functions as HTTP handlers.
type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
Registers the handler function for the given pattern in the DefaultServeMux.
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Query().Get("name"))
})

http.HandleFunc("/api/users", handleUsers)
Handle
func Handle(pattern string, handler Handler)
Registers the handler for the given pattern in the DefaultServeMux.

Request Handling

ResponseWriter
interface
Used by an HTTP handler to construct an HTTP response.
type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(statusCode int)
}
Usage:
func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"status":"ok"}`))
}

Status Codes

const (
    StatusOK                   = 200
    StatusCreated              = 201
    StatusAccepted             = 202
    StatusNoContent            = 204
    StatusMovedPermanently     = 301
    StatusFound                = 302
    StatusBadRequest           = 400
    StatusUnauthorized         = 401
    StatusForbidden            = 403
    StatusNotFound             = 404
    StatusMethodNotAllowed     = 405
    StatusInternalServerError  = 500
    StatusServiceUnavailable   = 503
)

Cookies

Adds a Set-Cookie header to the ResponseWriter’s headers.
cookie := &http.Cookie{
    Name:     "session",
    Value:    "abc123",
    Path:     "/",
    MaxAge:   3600,
    HttpOnly: true,
    Secure:   true,
}
http.SetCookie(w, cookie)
Returns the named cookie from the request.
cookie, err := r.Cookie("session")
if err == nil {
    fmt.Println("Session:", cookie.Value)
}

File Server

FileServer
func FileServer(root FileSystem) Handler
Returns a handler that serves HTTP requests with the contents of the file system rooted at root.
// Serve static files from ./static directory
fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
ServeFile
func ServeFile(w ResponseWriter, r *Request, name string)
Replies to the request with the contents of the named file or directory.
http.HandleFunc("/download", func(w http.ResponseWriter, r *http.Request) {
    http.ServeFile(w, r, "./files/document.pdf")
})

Examples

Simple HTTP GET

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
)

func main() {
    resp, err := http.Get("https://api.github.com")
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    
    fmt.Println("Status:", resp.Status)
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

POST Request with JSON

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
)

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    user := User{Name: "Alice", Email: "[email protected]"}
    jsonData, _ := json.Marshal(user)
    
    resp, err := http.Post("https://api.example.com/users",
        "application/json", bytes.NewBuffer(jsonData))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    
    fmt.Println("Status:", resp.StatusCode)
}

Custom Client with Timeout

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "time"
)

func main() {
    client := &http.Client{
        Timeout: 5 * time.Second,
    }
    
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    
    req, _ := http.NewRequestWithContext(ctx, "GET", 
        "https://api.example.com/slow", nil)
    req.Header.Set("Authorization", "Bearer token123")
    
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    
    fmt.Println("Status:", resp.Status)
}

Simple HTTP Server

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    // Handle root path
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Welcome to the home page!")
    })
    
    // Handle /hello path
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        name := r.URL.Query().Get("name")
        if name == "" {
            name = "Guest"
        }
        fmt.Fprintf(w, "Hello, %s!", name)
    })
    
    // Handle /api/status path
    http.HandleFunc("/api/status", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, `{"status":"healthy","version":"1.0"}`)
    })
    
    fmt.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

REST API Server

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

var users = []User{
    {ID: 1, Name: "Alice"},
    {ID: 2, Name: "Bob"},
}

func getUsers(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}

func createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    user.ID = len(users) + 1
    users = append(users, user)
    
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

func main() {
    http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "GET":
            getUsers(w, r)
        case "POST":
            createUser(w, r)
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })
    
    fmt.Println("API server running on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Build docs developers (and LLMs) love