If you’ve been building a web server in Go, at some point you will need to figure out how to serve your site with HTTPS.
One option is to use a load-balancing reverse-proxy such as Træfik or Caddy, letting it handle HTTPS while your application server(s) remain on HTTP.
If, however, your Go code needs to serve HTTPS directly, for example to take advantage of HTTP/2 Server Push, the main difficulty you will encounter is managing TLS certificates. There are two main ways to handle this: bring your own certificate, and automatic certificate management using a certification authority (CA) that supports the Automatic Certificate Management Environment (ACME) such as Let’s Encrypt.
Both options are straightforward to accomplish with Go. You can get the example code for this post with:
go get -d pocketgophers.com/serving-https
Run each example with:
go run example.go
example.go with the example you want to run.
Since you are starting from a working HTTP server, so will I:
This is a basic web server that runs on port
http (i.e., 80) and responds to every request with
Hello, 世界. Your code may vary, especially if you are not directly using
net/http. Take the time now to recreate this example using the same environment your web server uses so you will know how the examples in this post relate to your own code.
Most of the code in this example will not change. All changes will affect the line:
The way errors are handled (with
log.Fatal) and the
http.Handler used (
nil, meaning to use
http.DefaultServeMux) are not important for the purposes of this post.
Now that we have a common starting point, let’s make this web server serve HTTPS.
If you have a certificate¶
Your certificate and matching private keys are stored in
key.pem. These are paths to files. If your certificate and key are not stored on disk, look at the docs and code for *Server.ListenAndServeTLS(certFile, keyFile string) to start figuring out how to handle your situation.
It is your responsibility to handle all aspects of the certificate, especially updating it before it expires. The method shown in this example requires your web server to be restarted to load the new certificate. Use
tls.Config.GetCertificate to implement a more dynamic method.
If you don’t have a certificate¶
… you can get one and have it automatically updated before it expires by using golang.org/x/crypto/acme/autocert. This solution relies on three components:
autocert.Manageracquires, renews, and caches certificates
- HTTP server responds to HTTP-01 challenges and (by default) redirects other requests to HTTPS. This became a requirement after TLS-SNI-01 validation was disabled.
- HTTPS server responds with your handlers, using certificates supplied by an
This will starts web servers on ports 80 (HTTP) and 443 (HTTPS), and automatically acquires and renew certificates with the following defaults:
- Use Let’s Encrypt as the CA
- Automatically accept Let’s Encrypt’s terms of service
- Only handle certificates for the listed domains (e.g.,
- Cache certificates in
golang-autocert(this is a path to a directory)
- Renew certificates 30 days before expiration
NOTE: your server must be accessible on the public internet on ports 80 and 443, and have its domain name findable by the CA’s server. These are needed to prove to the CA that the server requesting the certificate serves the domain name and it is therefore reasonable to issue the certificate.