net/http is a full HTTP client and server, no framework required.
Examples
Bare ListenAndServe sets no timeouts. Configure a *Server.
mux := http.NewServeMux()
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("{\"status\":\"ok\"}"))
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
log.Fatal(server.ListenAndServe())$ curl localhost:8080/health
{"status":"ok"}Go 1.22+ patterns include method and path wildcards.
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "get user %s", r.PathValue("id"))
})
mux.HandleFunc("DELETE /users/{id}", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
})
http.ListenAndServe(":8080", mux)$ curl localhost:8080/users/42
get user 42No WebSocket parser in stdlib, Hijacker hands you the raw connection.
func wsHandler(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Upgrade") != "websocket" {
http.Error(w, "expected websocket upgrade", http.StatusBadRequest)
return
}
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "server doesn't support hijacking", http.StatusInternalServerError)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer conn.Close()
bufrw.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
bufrw.WriteString("Upgrade: websocket\r\n")
bufrw.WriteString("Connection: Upgrade\r\n\r\n")
bufrw.Flush()
}HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: UpgradeFunctions
| Function | Description |
|---|---|
NewServeMux() *ServeMux mux := http.NewServeMux() | Empty router. Register routes on the returned value. |
| |
ListenAndServe(addr string, handler Handler) error | Blocks. Error only on shutdown. |
The one-line form sets no timeouts, so a client that opens a connection and then says nothing holds it open indefinitely. Building the | |
ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error http.ListenAndServeTLS(":443", crt, key, mux) | The same, over HTTPS. |
| |
Serve(l net.Listener, handler Handler) error http.Serve(ln, mux) | Serves on a listener you opened yourself. |
| |
Error(w ResponseWriter, error string, code int) | Writes status and message, as plain text. |
The call writes the status and the message and then hands control straight back to your handler, which carries on to the next line. The | |
NotFound(w ResponseWriter, r *Request) http.NotFound(w, r) | Writes a 404 with a plain body. |
| |
Redirect(w ResponseWriter, r *Request, url string, code int) http.Redirect(w, r, "/login", 302) | Sets Location and writes the status. |
| |
SetCookie(w ResponseWriter, cookie *Cookie) http.SetCookie(w, &http.Cookie{Name: "s", Value: v}) | Adds one Set-Cookie header. |
| |
ServeFile(w ResponseWriter, r *Request, name string) http.ServeFile(w, r, "./static/logo.png") | Sends one file, handling ranges and caching. |
| |
ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) http.ServeContent(w, r, "a.txt", t, bytes.NewReader(b)) | ServeFile, for content that isn't a file. |
| |
FileServer(root FileSystem) Handler http.Handle("/static/", h) | Serves a whole directory tree. |
| |
StripPrefix(prefix string, h Handler) Handler http.StripPrefix("/static/", fs) | Trims the mux's prefix before the inner handler sees it. |
| |
MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser r.Body = http.MaxBytesReader(w, r.Body, 1<<20) | Caps how much of a body will be read. |
| |
DetectContentType(data []byte) string ct := http.DetectContentType(b) | Sniffs a MIME type from the first 512 bytes. |
| |
StatusText(code int) string http.StatusText(404) | The reason phrase, "Not Found". |
| |
Get(url string) (*Response, error) resp, err := http.Get(u) | defer resp.Body.Close() once err is nil. |
| |
Post(url, contentType string, body io.Reader) (*Response, error) resp, err := http.Post(u, "application/json", r) | A POST with a body you supply. |
| |
PostForm(url string, data url.Values) (*Response, error) resp, err := http.PostForm(u, url.Values{"q": {"go"}}) | A POST of form-encoded values. |
| |
NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error) | The request builder to reach for. |
Building the request rather than calling | |
NewRequest(method, url string, body io.Reader) (*Request, error) req, err := http.NewRequest("GET", u, nil) | The same, with no context attached. |
| |
ParseCookie(line string) ([]*Cookie, error) cs, err := http.ParseCookie(raw) | Parses a raw Cookie header. |
| |
Types
Open a row for the type's full method set.
| Type | Description |
|---|---|
type ServeMux struct4 methods | The router. Matches a request to a handler. |
Handle(pattern string, handler Handler) Registers a Handler. Panics on a duplicate or malformed pattern, at startup rather than at request time. HandleFunc(pattern string, handler func(ResponseWriter, *Request)) The same, taking a plain function. This is the one almost every route uses. Handler(r *Request) (h Handler, pattern string) Reports which handler a request would reach, without serving it. ServeHTTP(w ResponseWriter, r *Request) What makes the mux itself a Handler, so one mux can be mounted inside another. Satisfies http.Handler A pattern carries the method and the path together, and a brace-wrapped segment captures a value that | |
type Server struct8 methods | The server, with the timeouts ListenAndServe leaves unset. |
ListenAndServe() error Opens Addr and serves. Returns ErrServerClosed after Shutdown or Close. ListenAndServeTLS(certFile, keyFile string) error The same, over TLS. Serve(l net.Listener) error Serves on a listener you already opened. ServeTLS(l net.Listener, certFile, keyFile string) error Serves TLS on a listener you already opened. Shutdown(ctx context.Context) error Stops accepting, then waits for in-flight requests to finish or for ctx to expire. This is the graceful one. Close() error Drops every connection immediately, in-flight requests included. RegisterOnShutdown(f func()) Runs f when Shutdown starts, for cleanup that has to begin before the last request lands. SetKeepAlivesEnabled(v bool) Turns HTTP keep-alives off, so each connection serves one request. Building it yourself is what gets you timeouts and a shutdown you control. | |
type Request struct21 methods | The incoming request, or the outgoing one you build. |
Context() context.Context Cancelled when the client goes away. Pass it to every database and outbound call the handler makes. WithContext(ctx context.Context) *Request A shallow copy carrying a different context. This is how middleware attaches a request id or a user. Clone(ctx context.Context) *Request A deep copy, with the header map copied too, so writing to it cannot affect the original. PathValue(name string) string The value a wildcard captured, so "GET /users/{id}" gives you r.PathValue("id"). SetPathValue(name, value string) Sets one, which is mostly useful for building a request in a test. FormValue(key string) string One value from the query string or the form body. Calls ParseForm for you and swallows its error. PostFormValue(key string) string The same, ignoring the query string, so a URL parameter cannot shadow a form field. ParseForm() error Fills r.Form and r.PostForm. Call it directly when you want the parse error. ParseMultipartForm(maxMemory int64) error Parses a file upload, keeping maxMemory bytes in RAM and spilling the rest to temporary files. FormFile(key string) (multipart.File, *multipart.FileHeader, error) One uploaded file. Close the multipart.File when you are done with it. MultipartReader() (*multipart.Reader, error) Streams the parts instead of buffering them, for an upload too large to hold. Cookie(name string) (*Cookie, error) One cookie, or ErrNoCookie. Cookies() []*Cookie Every cookie the client sent. CookiesNamed(name string) []*Cookie Every cookie under one name, for the case where a client sent duplicates. AddCookie(c *Cookie) Adds a cookie to a request you are about to send. BasicAuth() (username, password string, ok bool) Decodes the Authorization header. Compare the results with subtle.ConstantTimeCompare. SetBasicAuth(username, password string) Sets that header on an outgoing request. UserAgent() string The User-Agent header. Referer() string The Referer header, misspelled in the protocol itself. ProtoAtLeast(major, minor int) bool Whether the request is at least that HTTP version. Write(w io.Writer) error Writes the request in wire format, which is what makes it easy to log or to replay. The context is the one to pass down into every database and outbound call, since it is cancelled the moment the client disconnects. | |
type ResponseWriter interface3 methods | How a handler answers. Three methods, in a fixed order. |
Header() Header The header map to write into. Set everything you need before the first Write. Write([]byte) (int, error) Writes body bytes. The first call sends a 200 and sniffs a content type if you set neither. WriteHeader(statusCode int) Sends the status. Calling it twice logs a warning and the second call is ignored. Implemented by the unexported type the server hands your handler, plus httptest.ResponseRecorder Order matters and it is not enforced. Headers first, then the status, then the body. A header set after the first | |
type Handler interface1 method | One method. Everything that serves a request is one. |
ServeHTTP(ResponseWriter, *Request) Writes the response. Returning from it ends the request, so a handler that writes an error has to return on the next line. Implemented by *ServeMux, HandlerFunc, the result of FileServer and StripPrefix One method is all it takes, which is why middleware is just a function from | |
type HandlerFunc func(ResponseWriter, *Request)1 method | Turns a plain function into a Handler. |
ServeHTTP(w ResponseWriter, r *Request) Calls f(w, r). Satisfies http.Handler The conversion is what turns a function into something with a method set. | |
type Client struct6 methods | The other side. Set its Timeout. |
Do(req *Request) (*Response, error) Sends a request you built. The one to use, since it is the only form that carries headers and a context. Get(url string) (resp *Response, err error) A GET, for when there is nothing to configure. Head(url string) (resp *Response, err error) Headers only, no body. Post(url, contentType string, body io.Reader) (resp *Response, err error) A POST with a body. PostForm(url string, data url.Values) (resp *Response, err error) A form-encoded POST. CloseIdleConnections() Drops pooled connections that are sitting idle. Declare one and keep it. The connection pool lives on the client, so a fresh one per request throws away every connection it opens and the timeout is the only thing standing between you and a call that never returns. | |
type Response struct4 methods | What comes back. Close the Body. |
Cookies() []*Cookie The cookies the server set. Location() (*url.URL, error) The Location header resolved against the request URL, for following a redirect by hand. ProtoAtLeast(major, minor int) bool Whether the response is at least that HTTP version. Write(w io.Writer) error Writes the response in wire format. A 404 arrives as a response, not an error, so the status check is yours to write. The body has to be closed on every path where the error was nil, including the ones where you decide not to read it. | |
type Header map[string][]string7 methods | The header map, on both a request and a response. |
Get(key string) string The first value, or "" when the header is absent. Set(key, value string) Replaces whatever was there. Add(key, value string) Appends, for a header that legitimately repeats such as Set-Cookie. Del(key string) Removes the entry. Values(key string) []string Every value under one key. Clone() Header A deep copy, so the original cannot be changed through it. Write(w io.Writer) error Writes the headers in wire format. Go through the methods rather than the map. They canonicalize the key, so a lookup for a lowercase name still finds the header. | |
type Cookie struct2 methods | One cookie, going out or coming in. |
String() string The serialized cookie, as a Set-Cookie value. Valid() error Reports whether the name, value, and attributes are legal. Satisfies fmt.Stringer Three fields carry the security. | |
type ResponseController struct5 methods | Reaches past the ResponseWriter interface, without a type assertion. |
Flush() error Pushes buffered output to the client, which is what makes server-sent events arrive as they happen. Hijack() (net.Conn, *bufio.ReadWriter, error) Hands over the raw connection. The server stops managing it, and closing it is yours. SetReadDeadline(deadline time.Time) error Overrides the server's ReadTimeout for this request. SetWriteDeadline(deadline time.Time) error Overrides WriteTimeout for this request, for a long download or a streaming response. EnableFullDuplex() error Lets an HTTP/1 handler read the request body while it is writing the response. This is how streaming works now. | |
type Hijacker interface1 method | Type-assert w to get the raw connection. |
Hijack() (net.Conn, *bufio.ReadWriter, error) The connection, and a buffered reader that may already hold bytes the server read ahead. Implemented by the HTTP/1 response writer. HTTP/2 does not support it After the call the server is finished with the connection and writing the protocol is yours, closing included. HTTP/2 does not support it, which is why the assertion has to be checked. | |
type Flusher interface1 method | Sends buffered output without waiting for the handler to return. |
Flush() No error to check. A writer that cannot flush does nothing. Implemented by the HTTP/1 and HTTP/2 response writers No error to check, so a writer that cannot flush does nothing and says nothing. That silence is the reason | |
type RoundTripper interface1 method | One request, one response, no redirects or cookies. |
RoundTrip(*Request) (*Response, error) Returns a non-nil error only when it could not get a response at all. A 500 is a response. Implemented by *http.Transport, which is what DefaultClient uses Implementing it wraps every request a client sends, which is where retries, logging, and a stub for tests belong. Clone the request before changing it, since the caller still owns the one handed in. | |
type CookieJar interface2 methods | Storage a Client keeps cookies in between calls. |
SetCookies(u *url.URL, cookies []*Cookie) Called with whatever a response set. Cookies(u *url.URL) []*Cookie Called to decide what to send with the next request to that URL. Implemented by *cookiejar.Jar A client with no jar forgets every cookie it receives, which is why a login on one call is gone by the next. | |
Values
| Value | Description |
|---|---|
StatusOK, StatusCreated, StatusNoContent int w.WriteHeader(http.StatusCreated) | 200, 201, 204. |
| |
StatusMovedPermanently, StatusFound, StatusSeeOther int http.Redirect(w, r, "/", http.StatusSeeOther) | 301, 302, 303. |
| |
StatusBadRequest, StatusUnauthorized, StatusForbidden, StatusNotFound int http.Error(w, "no", http.StatusForbidden) | 400, 401, 403, 404. |
| |
StatusMethodNotAllowed, StatusConflict, StatusUnprocessableEntity int w.WriteHeader(http.StatusConflict) | 405, 409, 422. |
StatusTooManyRequests int w.WriteHeader(http.StatusTooManyRequests) | 429. |
| |
StatusInternalServerError, StatusServiceUnavailable int http.Error(w, "server error", 500) | 500, 503. |
| |
MethodGet, MethodPost, MethodPut, MethodPatch, MethodDelete string http.NewRequest(http.MethodPost, u, body) | The method names, as strings. |
| |
DefaultClient *Client http.DefaultClient.Do(req) | What http.Get uses. No timeout. |
| |
ErrServerClosed error errors.Is(err, http.ErrServerClosed) | The server stopped on purpose. |
| |
ErrNoCookie error errors.Is(err, http.ErrNoCookie) | The request had no cookie under that name. |