Post

Go http.Redirect(...) set redirection manually with headers

How to manually implement HTTP redirects in Go by setting the Location header and writing the status code directly, instead of using http.Redirect.

This example shows how to implement HTTP redirects manually in Go without using http.Redirect. Instead of calling the helper function, the code explicitly sets the Location header with w.Header().Set("Location", "/") and then writes the status code with w.WriteHeader(). This gives you fine-grained control over the redirect behavior and helps illustrate what http.Redirect does internally.

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main

import (
    "fmt"
    "html/template"
    "net/http"
)

// Redirects:
//  - StatusMultipleChoices   = 300 // RFC 7231, 6.4.1
//  - StatusMovedPermanently  = 301 // RFC 7231, 6.4.2
//  - StatusFound             = 302 // RFC 7231, 6.4.3
//  - StatusSeeOther          = 303 // RFC 7231, 6.4.4
//  - StatusNotModified       = 304 // RFC 7232, 4.1
//  - StatusUseProxy          = 305 // RFC 7231, 6.4.5
//  - StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
//  - StatusPermanentRedirect = 308 // RFC 7538, 3

var tpl *template.Template

func init() {
    tpl = template.Must(template.ParseFiles("index.gohtml"))
}

func main() {
    http.HandleFunc("/", foo)
    http.HandleFunc("/bar", bar)
    http.HandleFunc("/barred", barred)
    http.Handle("/favicon.ico", http.NotFoundHandler())
    http.ListenAndServe(":8080", nil)

}

// Options for <form> and POST method in particular
// 	- <form method="POST" enctype="multipart/form-data">
// 	- <form method="POST" enctype="multipart/x-www-form-urlencoded">
// 	- <form method="POST" enctype="text/plain">

func foo(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("Foo, request method: %v\n", r.Method)
    // io.WriteString(w, "foo was called ...")
}

func bar(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("Bar, request method: %v\n", r.Method)

	// http.Redirect(w, r, "/", http.StatusTemporaryRedirect) // status code: 307
	// Equivalent of the command above ^^^

	w.Header().Set("Location", "/")

	// Option 1.
	// 307 does not change the METHOD
	// w.WriteHeader(http.StatusTemporaryRedirect)

	// Option 2.
	// 303 change the METHOD to GET
	w.WriteHeader(http.StatusSeeOther)
}

func barred(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("Barred, request method: %v\n", r.Method)
    tpl.ExecuteTemplate(w, "index.gohtml", nil)
    // io.WriteString(w, "barred was called ...")
}


// <!DOCTYPE html>
// <html lang="en">
// <head>
//     <meta charset="UTF-8">
//     <title>Status StatusTemporaryRedirect 307 </title>
// </head>
// <body>


// <form method="POST" action="/bar" enctype="text/plain">
//     <label for="idx-f">First Name</label>
//     <input type="text" id="idx-f" name="first"><br>
//     <input type="submit">
// </form>

// </body>
// </html>
This post is licensed under CC BY 4.0 by the author.