In Ruby/Rack, I'm able to get the scheme of the current request URL from scheme#request
. However, in Go, http.Request.URL.Scheme
returns an empty string:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%#v\n", r.URL.Scheme) // Always shows empty string
}
How do I get scheme of the current request URL?
A quick grep
shows that r.URL.Scheme
is never set to anything other than the empty string anywhere in net/http
. Personally I think it should be, as far as possible, but apparently I have a minority opinion.
If you opened a TLS listener yourself with http.ListenAndServeTLS()
then presumably you know the scheme is https already. You can use a trivial middleware handler that fills in r.URL.Scheme
in this case.
func AlwaysHTTPS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.URL.Scheme = "https"
next.ServeHTTP(w, r)
})
}
If you're running behind a web server, then it may pass the request protocol in a header such as X-Forwarded-Proto
. In this case, you can use a handler like gorilla's handlers.ProxyHeaders() to fill in the missing fields.
An example using gorilla mux:
package main
import (
"log"
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.Use(handlers.ProxyHeaders)
http.Handle("/", r)
log.Fatal(http.ListenAndServe("[::]:8009", nil))
}
From its comments:
ProxyHeaders inspects common reverse proxy headers and sets the corresponding fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP for the remote (client) IP address, X-Forwarded-Proto or X-Forwarded-Scheme for the scheme (http|https) and the RFC7239 Forwarded header, which may include both client IPs and schemes.
NOTE: This middleware should only be used when behind a reverse proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are configured not to) strip these headers from client requests, or where these headers are accepted "as is" from a remote client (e.g. when Go is not behind a proxy), can manifest as a vulnerability if your application uses these headers for validating the 'trustworthiness' of a request.