-
-
Save d-schmidt/587ceec34ce1334a5e60 to your computer and use it in GitHub Desktop.
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) | |
} |
If you want to also send the request body (like for POST requests), you should use 307 code (http.StatusTemporaryRedirect).
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.
@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.
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.
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, }
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?
@MakotoE you are probably right. I will add a another comment
@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, ':')]
}
generate test tls certs with go: https://github.com/golang/go/blob/master/src/crypto/tls/generate_cert.go