Go HTTP server
last modified April 11, 2024
In this article we show how to create simple HTTP servers in Golang.
HTTP
The Hypertext Transfer Protocol (HTTP) is an application protocol for distributed, collaborative, hypermedia information systems. HTTP protocol is the foundation of data communication for the World Wide Web.
Go http
In Go, we use the http package to create GET and POST requests.
The package provides HTTP client and server implementations.
Go http types
A client sends a request to a server to receive a resource.
type Request struct
A Request represents an HTTP request received by a server, sent by
a client.
type Response struct
Response represents the response from an HTTP request.
type ResponseWriter interface
A ResponseWriter interface is used by an HTTP handler to construct
an HTTP response.
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
A Handler responds to an HTTP request. The ServeHTTP
writes reply headers and data to the ResponseWriter and then
returns.
type HandlerFunc func(ResponseWriter, *Request)
The HandlerFunc type is an adapter that allows the use of ordinary
functions as HTTP handlers.
Go HTTP server Handle
The Handle function registers a handler for the given URL. The goal
of the handler is to create a reply to the client's request.
package main
import (
    "fmt"
    "net/http"
)
type CounterHandler struct {
    counter int
}
func (ct *CounterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(ct.counter)
    ct.counter++
    fmt.Fprintln(w, "Counter:", ct.counter)
}
func main() {
    th := &CounterHandler{counter: 0}
    http.Handle("/count", th)
    http.ListenAndServe(":8080", nil)
}
Each time the URL is visited, a counter is incremented and the value is returned.
type CounterHandler struct {
    counter int
}
func (ct *CounterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(ct.counter)
    ct.counter++
    fmt.Fprintln(w, "Counter:", ct.counter)
}
CounterHandler contains the counter variable and the 
implementation of the ServeHTTP function. The function increments 
the counter and writes a message to the http.ResponseWriter.
th := &CounterHandler{counter: 0}
http.Handle("/count", th)
CounterHandler is created and registered with Handle.
$ curl localhost:8080/count Counter: 1 $ curl localhost:8080/count Counter: 2 $ curl localhost:8080/count Counter: 3 $ curl localhost:8080/count Counter: 4
Go HTTP server HandleFunc
With HandleFunc function, we register a handler function for the
given URL pattern. HandleFunc functions are convenient ways to
create handlers.
package main
import (
    "fmt"
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/", HelloHandler)
    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
func HelloHandler(w http.ResponseWriter, _ *http.Request) {
    fmt.Fprintf(w, "Hello, there\n")
}
The example creates a simple HTTP server listening on port 8080. Upon sending a request, the server responds with a "Hello, there" message.
http.HandleFunc("/", HelloHandler)
We map the / pattern to the HelloHandler with
HandleFunc.
log.Fatal(http.ListenAndServe(":8080", nil))
The ListenAndServe listens on the TCP network address and then
handles requests on incoming connections.
func HelloHandler(w http.ResponseWriter, _ *http.Request) {
    fmt.Fprintf(w, "Hello, there\n")
}
The handler responds to an HTTP request. It takes two parameters: the response writer and the request object.
$ go run main.go
We start the server.
$ curl localhost:8080/ Hello, there
With the curl tool, we generate a request.
Go HTTP status code
The HTTP response status codes indicate whether a specific HTTP request has been successfully completed.
The responses are grouped in five classes:
- Informational responses (100–199)
- Successful responses (200–299)
- Redirects (300–399)
- Client errors (400–499)
- Server errors (500–599)
The status code for the response is written with the WriteHeader
function.
package main
import (
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/status", func(w http.ResponseWriter, _ *http.Request) {
        w.WriteHeader(http.StatusOK)
    })
    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
We send the http.StatusOK for the /status path.
$ curl -I localhost:8080/status HTTP/1.1 200 OK Date: Sat, 23 Apr 2022 12:59:52 GMT
Go HTTP Server not found handler
A 404 error code is returned to the client if the server resource cannot be found.
package main
import (
    "fmt"
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/about", func(w http.ResponseWriter, _ *http.Request) {
        fmt.Fprintln(w, "about page")
    })
    http.HandleFunc("/news", func(w http.ResponseWriter, _ *http.Request) {
        fmt.Fprintln(w, "news page")
    })
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/" {
            w.WriteHeader(404)
            w.Write([]byte("404 - not found\n"))
            return
        }
        fmt.Fprintln(w, "home page")
    })
    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
In the example, we have three endpoints. For anything other than these three endpoints, we return a 404 error message.
$ curl localhost:8080/about about page $ curl localhost:8080/ home page $ curl localhost:8080/contact 404 - not found
Go HTTP server get header
HTTP headers let the client and the server pass additional information with an HTTP request or response. An HTTP header is a name/value pair, separated by a colon character.
The User-Agent request header is a string that lets servers and network peers identify the application, operating system, vendor, and/or version of the requesting user agent.
package main
import (
    "fmt"
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/ua", func(w http.ResponseWriter, r *http.Request) {
        ua := r.Header.Get("User-Agent")
        fmt.Fprintf(w, "User agent: %s\n", ua)
    })
    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
From the Header field, we get the User-Agent
header and return it back to the caller.
package main
import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)
func main() {
    resp, err := http.Get("http://localhost:8080/ua")
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(body))
}
The example creates a simple client in Go, which generates a GET request to
the /ua path.
Go URL path parameter
We can send data to the server in the URL.
package main
import (
    "fmt"
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/", HelloServer)
    fmt.Println("Server started at port 8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
func HelloServer(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!\n", r.URL.Path[1:])
}
In the code example, we get the URL path value with r.URL.Path[1:]
and build a message with the data.
$ curl localhost:8080/John Hello, John!
We send the name inside the URL path; the server responds with a greeting.
Go query parameter
A query string is a part of a uniform resource locator (URL) that assigns values to specified parameters.
A generic URL has the following form:
scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
The query parameters start with the ? character. Multiple query
parameters are separated with & character.
https://example.com/path/page?name=John&occupation=teacher
This is an example of a URL with two query parameters.
package main
import (
    "fmt"
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/", handler)
    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
    keys, ok := r.URL.Query()["name"]
    name := "guest"
    if ok {
        name = keys[0]
    }
    fmt.Fprintf(w, "Hello %s!\n", name)
}
In the code example, we accept a name parameter. We get the parameter
with r.URL.Query()["name"].
$ curl localhost:8080/?name=Peter Hello Peter!
We send the name as a query parameter; the server responds with a message.
Go file server
With http.FileServer, we send files to the client.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>About page</title>
</head>
<body>
    <p>
        About page
    </p>
</body>
</html>
This is the about.html page.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>
    <p>
        Home page
    </p>
</body>
</html>
This is the home page.
package main
import (
    "fmt"
    "io"
    "log"
    "net/http"
)
func main() {
    fileServer := http.FileServer(http.Dir("./public"))
    http.Handle("/", fileServer)
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hello there!\n")
    })
    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
In the code example, we have a file server and a simple hello handler.
fileServer := http.FileServer(http.Dir("./public"))
http.Handle("/", fileServer)
A file server is registered with Handle; it serves files from
the public directory.
$ curl localhost:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>
    <p>
        Home page
    </p>
</body>
</html>
$ curl localhost:8080/about.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>About page</title>
</head>
<body>
    <p>
        About page
    </p>
</body>
</html>
We request the home page and the about page.
Go process GET/POST request
In the following example, the servers processes the GET and the POST requests from a client.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Form</title>
</head>
<body>
    <form method="POST" action="/">
        <div>
            <label>Name:</label><input name="name" type="text">
        </div>
        <div>
            <label>Occupation:</label><input name="occupation" type="text">
        </div>
        <button type="submit" value="submit">Submit</button>
    </form>
</body>
</html>
The HTML page presents a simple form.
package main
import (
    "fmt"
    "log"
    "net/http"
)
func process(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        http.Error(w, "404 not found.", http.StatusNotFound)
        return
    }
    switch r.Method {
    case "GET":
        http.ServeFile(w, r, "form.html")
    case "POST":
        if err := r.ParseForm(); err != nil {
            fmt.Fprintf(w, "ParseForm() err: %v", err)
            return
        }
        name := r.FormValue("name")
        occupation := r.FormValue("occupation")
        fmt.Fprintf(w, "%s is a %s\n", name, occupation)
    default:
        fmt.Fprintf(w, "Sorry, only GET and POST methods are supported.")
    }
}
func main() {
    http.HandleFunc("/", process)
    log.Println("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
For a GET request, we send a web page with a form. For a POST request, we process the data from the form.
case "GET":
    http.ServeFile(w, r, "form.html")
If the request is a GET request, we send the form.html to the
client.
case "POST":
    if err := r.ParseForm(); err != nil {
        fmt.Fprintf(w, "ParseForm() err: %v", err)
        return
    }
In case of a POST request, we call the ParseForm function; it
parses the raw query from the URL and updates r.Form.
name := r.FormValue("name")
occupation := r.FormValue("occupation")
fmt.Fprintf(w, "%s is a %s\n", name, occupation)
We get the form values with FormValue and build a message.
Go HTTP serve image
In the following example, we serve an image.
package main
import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)
func main() {
    handler := http.HandlerFunc(handleRequest)
    http.Handle("/image", handler)
    fmt.Println("Server started at port 8080")
    http.ListenAndServe(":8080", nil)
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
    buf, err := ioutil.ReadFile("sid.png")
    if err != nil {
        log.Fatal(err)
    }
    w.Header().Set("Content-Type", "image/png")
    w.Write(buf)
}
In the code example, we create a simple web server that sends an image to the client. The image is located in the current working directory.
handler := http.HandlerFunc(handleRequest)
http.Handle("/image", handler)
We map a handler to the /image path.
func handleRequest(w http.ResponseWriter, r *http.Request) {
...
The handler function accepts two parameters: http.ResponseWriter
and http.Request.
buf, err := ioutil.ReadFile("sid.png")
We read the image into the buffer.
w.Header().Set("Content-Type", "image/png")
We set the header. The Content-Type content type is used for PNG
image.
w.Write(buf)
The image data is written to the response body with Write.
Go HTTP server template
Go has a built-in template package for generating dynamic HTML content.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Users</title>
</head>
<body>
    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Occupation</th>
            </tr>
        </thead>
        <tbody>
            {{ range .Users}}
            <tr>
                <td>{{.Name}}</td>
                <td>{{.Occupation}}</td>
            </tr>
            {{ end }}
        </tbody>
    </table>
</body>
</html>
The output is an HTML file. The data is inserted into HTML table.
package main
import (
    "html/template"
    "log"
    "net/http"
)
type User struct {
    Name       string
    Occupation string
}
type Data struct {
    Users []User
}
func main() {
    tmp := template.Must(template.ParseFiles("layout.html"))
    http.HandleFunc("/users", func(w http.ResponseWriter, _ *http.Request) {
        data := Data{
            Users: []User{
                {Name: "John Doe", Occupation: "gardener"},
                {Name: "Roger Roe", Occupation: "driver"},
                {Name: "Peter Smith", Occupation: "teacher"},
            },
        }
        tmp.Execute(w, data)
    })
    log.Println("Listening...")
    http.ListenAndServe(":8080", nil)
}
The web server returns an HTML page with a table of users for the
/users URL path.
Source
Go net/http package - reference
In this article we have created simple HTTP servers in Go.
Author
List all Go tutorials.