Question
I am making a simple HTTP GET request in Go:
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
res, _ := client.Do(req)
How can I customize the request headers? I could not find a clear way to do that in the documentation.
Short Answer
By the end of this page, you will understand how to add custom headers to an HTTP GET request in Go using the net/http package. You will also learn the difference between creating a request and sending it, how headers are stored, common mistakes to avoid, and how this pattern is used in real Go codebases.
Concept
In Go, HTTP headers are part of the http.Request object. That means you do not pass headers directly into client.Do(). Instead, you:
- Create a request with
http.NewRequest() - Add or modify headers on the request
- Send the request with
client.Do()
A header is just metadata sent with the request. Common examples include:
Authorizationfor tokens or API keysAcceptto say what response format you wantUser-Agentto identify the clientContent-Typefor request bodies
In Go, headers are stored in req.Header, which is a map-like structure of type http.Header.
Example:
req.Header.Set("Authorization", "Bearer my-token")
req.Header.Set("Accept", "application/json")
This matters because many real APIs require headers for:
- authentication
- versioning
- content negotiation
- tracing and debugging
- rate limiting or client identification
If you do not set the required headers, the server may reject your request or return unexpected results.
Mental Model
Think of an HTTP request like mailing a package.
- The URL is the destination address.
- The method (
GET,POST) is the type of delivery. - The body is the package contents.
- The headers are the labels on the box.
For example:
Authorizationis like a security badge.Accept: application/jsonis like saying, "Please reply in JSON format."User-Agentis like writing who sent the package.
In Go, you first prepare the box (http.NewRequest), then attach labels (req.Header.Set(...)), and only then send it (client.Do(req)).
Syntax and Examples
The basic syntax is:
req, err := http.NewRequest("GET", url, nil)
if err != nil {
// handle error
}
req.Header.Set("Header-Name", "value")
res, err := client.Do(req)
if err != nil {
// handle error
}
defer res.Body.Close()
Example: Add custom headers to a GET request
package main
import (
"fmt"
"net/http"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.example.com/users", nil)
if err != nil {
fmt.Println("request creation error:", err)
return
}
req.Header.Set("Authorization", "Bearer my-token")
req.Header.Set("Accept", "application/json")
req.Header.Set("User-Agent", "my-go-app/1.0")
res, err := client.Do(req)
if err != nil {
fmt.Println("request error:", err)
}
res.Body.Close()
fmt.Println(, res.Status)
}
Step by Step Execution
Consider this example:
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.example.com/data", nil)
if err != nil {
return
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer abc123")
res, err := client.Do(req)
if err != nil {
return
}
defer res.Body.Close()
Here is what happens step by step:
-
client := &http.Client{}- Creates an HTTP client that can send requests.
-
http.NewRequest("GET", "https://api.example.com/data", nil)- Creates a new request object.
- The method is
GET. - The URL is
https://api.example.com/data. - The body is
nilbecause a simple GET request usually has no body.
-
req.Header.Set("Accept", "application/json")- Adds an header.
Real World Use Cases
Custom request headers are used in many practical situations:
Calling authenticated APIs
req.Header.Set("Authorization", "Bearer "+token)
Used when talking to services like GitHub, Stripe, or internal company APIs.
Asking for JSON responses
req.Header.Set("Accept", "application/json")
Useful when an API can return multiple formats.
Sending API keys
req.Header.Set("X-API-Key", apiKey)
Some services use custom headers instead of bearer tokens.
Identifying your client
req.Header.Set("User-Agent", "inventory-sync/2.1")
Helpful for logs, monitoring, or service rules.
Tracing requests across services
req.Header.Set("X-Request-ID", requestID)
Common in distributed systems and microservices.
Real Codebase Usage
In real Go projects, developers usually do more than just set one header manually.
Validation before sending requests
A common pattern is to validate required input first:
if token == "" {
return fmt.Errorf("missing token")
}
Then build the request and set headers.
Helper functions
Teams often wrap request creation in reusable functions:
func newAPIRequest(method, url, token string) (*http.Request, error) {
req, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/json")
return req, nil
}
This keeps header logic consistent.
Guard clauses and early returns
Instead of deeply nested code, Go codebases often use early error checks:
req, err := http.NewRequest("GET", url, )
err != {
err
}
res, err := client.Do(req)
err != {
err
}
Common Mistakes
Here are common beginner mistakes when setting headers in Go.
1. Forgetting to set headers on the request
Broken code:
client := &http.Client{}
res, err := client.Do(req)
req.Header.Set("Authorization", "Bearer token")
Problem:
- The request was already sent.
- Setting headers afterward does nothing for that request.
Fix:
req.Header.Set("Authorization", "Bearer token")
res, err := client.Do(req)
2. Ignoring errors from http.NewRequest
Broken code:
req, _ := http.NewRequest("GET", url, nil)
Problem:
- If request creation fails, you may get unexpected behavior later.
Fix:
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
3. Forgetting to close the response body
Broken code:
Comparisons
Here is how related HTTP request concepts compare in Go:
| Concept | Purpose | Example | Common Use |
|---|---|---|---|
req.Header.Set() | Set or replace a header value | req.Header.Set("Accept", "application/json") | Most common choice |
req.Header.Add() | Add another value for the same header | req.Header.Add("Cache-Control", "no-cache") | Multiple values when needed |
http.NewRequest() | Create a request object | http.NewRequest("GET", url, nil) | First step before sending |
client.Do(req) | Send the prepared request |
Cheat Sheet
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer token")
req.Header.Set("Accept", "application/json")
res, err := client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
Quick rules
- Use
req.Header.Set(key, value)to set a header. - Set headers before calling
client.Do(req). - Use
Setto replace a value. - Use
Addto append another value. - Always check errors from
http.NewRequestandclient.Do. - Always close
res.Body. - Use
Acceptfor expected response format. - Use
Content-Typemainly when sending a request body.
Common headers
Authorization
FAQ
How do I add headers to a GET request in Go?
Create the request with http.NewRequest(), then use req.Header.Set("Name", "Value") before sending it with client.Do(req).
Can I set headers with http.Get() in Go?
Not directly in a clean way. If you need custom headers, create a request manually with http.NewRequest().
What is the difference between Set and Add for headers in Go?
Set replaces any existing value. Add appends another value under the same header name.
Do I need Content-Type for a GET request?
Usually no, because GET requests often have no body. Accept is often more useful for GET requests.
Why is my Authorization header not being sent?
A common reason is setting the header after calling client.Do(req). Headers must be added before sending the request.
Are header names case-sensitive in Go?
Go handles headers in a canonical form, and HTTP header names are generally treated case-insensitively. Still, use standard names like and for clarity.
Mini Project
Description
Build a small Go program that fetches user data from an API endpoint using a custom Authorization header and an Accept header. This demonstrates the full request flow: creating a request, setting headers, sending it, and handling the response safely.
Goal
Create a GET request in Go that includes custom headers and prints the HTTP status returned by the server.
Requirements
- Create an
http.Clientand build a GET request withhttp.NewRequest - Add at least two headers:
AuthorizationandAccept - Send the request using
client.Do(req) - Check for errors when creating and sending the request
- Close the response body after the request completes
Keep learning
Related questions
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Calling Functions Across Files in the Same Go Package
Learn how Go uses packages across multiple files, why functions may appear undefined, and how to organize code correctly.