Cookies management by TermsFeed Cookie Consent
Russia has invaded Ukraine and already killed tens of thousands of civilians, with many more raped or tortured. It's a genocide. We need your help. Let's fight back against the Russian regime.
Help Ukraine! Fight the Russian regime!

🐜 Print HTTP request/response for debugging in Go

http

Please consider supporting us by disabling your ad blocker

When writing an HTTP server or client in Go, it is often useful to print the full HTTP request or response to the standard output for debugging purposes. Using fmt.Println() may not be sufficient in this case because the output is not formatted and difficult to read. The better idea is to use the httputil.DumpRequest(), httputil.DumpRequestOut() and httputil.DumpResponse() functions which were created to pretty-print the HTTP request and response. Such pretty-printing or dumping means that the request/response is presented in a similar format as it is sent to and received from the server.

The rules for using these functions are simple:

Log client requests and responses

Check the example below to learn how to dump the HTTP client request and response using the httputil.DumpRequestOut() and httputil.DumpResponse() functions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

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

const serverAddr = "http://example.com/"

func main() {
    req, err := http.NewRequest(http.MethodGet, serverAddr, nil)
    if err != nil {
        log.Fatal(err)
    }
    req.Header.Add("test-header", "test-header-value")

    reqDump, err := httputil.DumpRequestOut(req, true)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("REQUEST:\n%s", string(reqDump))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        log.Fatal(err)
    }

    respDump, err := httputil.DumpResponse(resp, true)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("RESPONSE:\n%s", string(respDump))
}

Output:

REQUEST:
GET / HTTP/1.1
Host: example.com
User-Agent: Go-http-client/1.1
Test-Header: test-header-value
Accept-Encoding: gzip

RESPONSE:
HTTP/1.1 200 OK
Accept-Ranges: bytes
Age: 357425
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Fri, 25 Feb 2022 06:48:03 GMT
Etag: "3147526947+gzip"
Expires: Fri, 04 Mar 2022 06:48:03 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECS (bsa/EB18)
Vary: Accept-Encoding
X-Cache: HIT

<!doctype html>
<html>
<head>
    <title>Example Domain</title>

    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style type="text/css">
...

Log requests on the server side

From the example below, you can find out how to pretty-print an incoming server request using the httputil.DumpRequest() function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

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

const port = 8080

func indexHandler(w http.ResponseWriter, r *http.Request) {
    reqDump, err := httputil.DumpRequest(r, true)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("REQUEST:\n%s", string(reqDump))
    w.Write([]byte("Hello World"))
}

func main() {
    http.HandleFunc("/", indexHandler)
    log.Printf("Starting HTTP server at port: %d\n", port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
}

Output:

2022/02/25 07:03:20 Starting HTTP server at port: 8080
REQUEST:
GET / HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,en-US;q=0.8,en;q=0.7
Connection: keep-alive
Dnt: 1
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36

REQUEST:
GET /favicon.ico HTTP/1.1
Host: localhost:8080
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,en-US;q=0.8,en;q=0.7
Connection: keep-alive
Dnt: 1
Referer: http://localhost:8080/
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36

📔 Convert a struct to io.Reader in Go

Learn how to convert a struct to io.Reader and send it as an HTTP POST request body
introduction http

📟 Convert HTTP response io.ReadCloser to string in Go

Learn how to convert a HTTP client response body to string
introduction http

🔌 Handle 'connection reset by peer' error in Go

Learn what it means and how to detect the 'connection reset by peer' error
http errors