Skip to content

Instantly share code, notes, and snippets.

@d-schmidt
Last active March 12, 2024 08:04
Show Gist options
  • Save d-schmidt/587ceec34ce1334a5e60 to your computer and use it in GitHub Desktop.
Save d-schmidt/587ceec34ce1334a5e60 to your computer and use it in GitHub Desktop.
How to redirect HTTP to HTTPS with a golang webserver.
package main
import (
"net/http"
"log"
)
func redirect(w http.ResponseWriter, req *http.Request) {
// remove/add not default ports from req.Host
target := "https://" + req.Host + req.URL.Path
if len(req.URL.RawQuery) > 0 {
target += "?" + req.URL.RawQuery
}
log.Printf("redirect to: %s", target)
http.Redirect(w, req, target,
// see comments below and consider the codes 308, 302, or 301
http.StatusTemporaryRedirect)
}
func index(w http.ResponseWriter, req *http.Request) {
// all calls to unknown url paths should return 404
if req.URL.Path != "/" {
log.Printf("404: %s", req.URL.String())
http.NotFound(w, req)
return
}
http.ServeFile(w, req, "index.html")
}
func main() {
// redirect every http request to https
go http.ListenAndServe(":80", http.HandlerFunc(redirect))
// serve index (and anything else) as https
mux := http.NewServeMux()
mux.HandleFunc("/", index)
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", mux)
}
@d-schmidt
Copy link
Author

@andreiavrammsd I've changed the example to 307. A GET only server should use 301. Browsers will cache this redirect.
@drstearns using only req.URL.Path would omit query parameters. I've updated the example anyways.
I've added a comment for the possible port problem.

@d-schmidt
Copy link
Author

The docs do not recommend to use req.RequestURI. Never trust the client.
RFC 2616, Section 5.1.2: * is a valid RequestURI.
https://golang.org/pkg/net/http/#Request

// RequestURI is the unmodified Request-URI of the
// Request-Line (RFC 2616, Section 5.1) as sent by the client
// to a server. Usually the URL field should be used instead.

@AneudyM
Copy link

AneudyM commented Mar 30, 2018

You could also use the url package to construct the URL as well:

targetUrl := url.URL{ Scheme: "https", Host: r.Host, Path: r.URL.Path, RawQuery: r.URL.RawQuery, }

@MakotoE
Copy link

MakotoE commented Aug 15, 2019

Shouldn't you use http.StatusPermanentRedirect (308) as the status code for redirect, not http.StatusTemporaryRedirect (307), because you're probably not going to create an unsecure http route in the future?

@d-schmidt
Copy link
Author

d-schmidt commented Aug 18, 2019

@MakotoE you are probably right. I will add a another comment

@pystub
Copy link

pystub commented Nov 29, 2021

@drstearns it flops on ipv6 addresses.

This is what I came up with:

d := r.Host
if m, _ := regexp.MatchString(`:\d+$`, d); m {
	d = d[:strings.LastIndexByte(d, ':')]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment