Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link
Owner Author

commented Oct 5, 2015

generate test tls certs with go: https://github.com/golang/go/blob/master/src/crypto/tls/generate_cert.go

generate_cert -host=127.0.0.1,localhost,example.com -ca -ecdsa-curve=P384
@andreiavrammsd

This comment has been minimized.

Copy link

commented Dec 28, 2016

If you want to also send the request body (like for POST requests), you should use 307 code (http.StatusTemporaryRedirect).

@drstearns

This comment has been minimized.

Copy link

commented Jan 15, 2017

One thing to watch out for with this solution: if your HTTP server is running on a non-standard port (i.e., not 80), the req.Host value will be the host name and HTTP port number (e.g., example.com:8000). So you should strip off any port number that might be on req.Host so that the browser doesn't try to establish an HTTPS connection on the wrong port. You can do this using a line like:

host := strings.Split(req.Host, ":")[0]

And if your HTTPS server is running on a non-standard port, you need to append that to the host value so the browser knows which port to use.

It also might be slightly safer to use req.URL.Path for the rest of the URL path, rather than req.URL.String(). If the HTTP request included an absolute URL in the first line, req.URL.String() would return that entire URL, complete with scheme and host.

@d-schmidt

This comment has been minimized.

Copy link
Owner Author

commented Jan 16, 2017

@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.

@Justin-Marks

This comment has been minimized.

Copy link

commented Feb 24, 2017

There's a function for splitting the host:port https://golang.org/pkg/net/#SplitHostPort
Using req.RequestURI is probably simpler and more reliable than using req.URL.String() or req.URL.Path.

@d-schmidt

This comment has been minimized.

Copy link
Owner Author

commented Feb 25, 2017

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.
@Justin-Marks

This comment has been minimized.

Copy link

commented Mar 1, 2017

Usually the URL field should be used instead.

URL is parsed from RequestURI. It's still using the same data.
The http handler accepts any gibberish. You'd still have to validate the input yourself in either case.

But RequestURI does pose the same problem as URL.String() for absolute URLs so maybe it's not so good afterall. URL.Path is looking better now.

@AneudyM

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Owner Author

commented Aug 18, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.