I've never been any kind of expert on software security, but I've always known that HTTPS beats HTTP at all times. I know that the communication in the latter is not encrypted and your data can be stolen. I always thought that it takes a great deal of effort to secure the connection, but lately, I discovered how little you actually need, thanks to Let's Encrypt and Caddy.

When you access a page via insecure HTTP connection, there is a possibility that someone can perform a man in the middle attack, where they intercept data send from one side and pass it forward, possibly changed, to the other. You can, for example, lose your passwords or any sensible data, really!

For a long time, having an SSL certificate (that allows you to add s to your http) cost some money and it forced everybody to ask themselves if their users' data is precious enough for them to pay for a secure connection. Unfortunately, in most of the cases they decided to turn their heads because whenever you can save your money and time, people are going to do that. Then it came April 12, 2016, and Let's Encrypt was launched - a free, automated, and open certificate authority (CA), run for the public’s benefit. It's idea was (and still is) to provide everyone with free certificates that enable HTTPS as easily as possible.

When I first saw that, I was super excited since I wanted to build a secure website so badly. I played with it for a couple of hours, but I failed to do anything. I can't remember exactly what happened, but I must have been doing something wrong, and because of my imposter syndrome I assumed that Let's Encrypt is great and I'm just not good enough to use it. It was probably true, but it just exposed a problem, that it might not be as easy as they might like.

The sun came out for my dreams of HTTPS when I discovered Caddy, and this time it went smoother than I would imagine.

Introducing Caddy

Caddy is a project created by Matt Holt and it was created with the same idea in mind as Let's Encrypt. It's webpage screams: EVERY site on HTTPS, and thanks to an extremely simple configuration you cannot have any excuses that it's too difficult and it requires too much work to secure your page.

The first thing you need to do is download Caddy binary to your server host machine. You can do that from here, and you can customise your download, just as you could to for example with nginx. For now, though, we'll skip all the fireworks and add-ons that Caddy offers and get the simplest version.

Note that using Caddy for free is possible only for personal use. For commercial usage, you'll have to pay, but it's still a pretty good value for money.

Next step is creating a configuration file, called Caddyfile that describes what and how things should be served. Note that the usage of Let's Encrypt is implicit, so you don't have to specify it anyhow, it will work out of the box.

The important thing for Let's Encrypt to work is that since it is automated, our page has to be accessible from the outside world for verification. The way it works, simplified a bit, is that LE needs to check if we, asking for a cert, and we, the owners of a server, are the same people. To do that they require our page to serve a specific content under some specific URL. This also requires us to have some domain, so keep this in mind.

My testing domain will be altered below, so that nobody DDoSes it (:]), but it's been tested and it works.

Serving static page

Let's start with something extremely simple: create a static HTML page and serve it behind HTTP. To do that, our Caddyfile need to specify a group identified by our domain name and 443 port that represents secured version of HTTP:

# Caddyfile
m***.pl:443 {
    root www
}

In order to serve static content, all you need to do is define a root attribute that represents a directory containing the HTML, JS and CSS files. If I create an HTML file:

<!-- www/index.html -->
Hello Secure World!

... and run Caddy (with Caddyfile in the same directory) ...

$ nohup caddy &

... I can see a green lock:

Proxying Go webapp

Since we succeeed with a static content, let's go further and proxy a Go web application so that it's behind secured connection as well. To do that we'll create the simplest app:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
        fmt.Fprint(rw, "Hello Secure Go World!")
    })
    http.ListenAndServe(":4444", nil)
}

We now build the binary, send it over to the server and run. Since it works on port 4444, we can now introduce another property to our Caddy configuration, proxy, which takes two parameters. First is a path where the proxied app will be available to the user (ourdomain.com/this-path), and the second one is where the app is accessible within the server (localhost:4444 in our case):

# Caddyfile 
m***.pl:443 {
    proxy / localhost:4444    
}

Now when we run the server...

$ nohup caddy &

... we can see our Go application behind HTTPS as well:

Note, that the Go code didn't have to know anything about the certificate from Let's Encrypt since we used http.ListenAndServe(...) designed for HTTP connections. This means that all your old, insecure web applications can be proxied as well, without you having to change anything.

Dropping the S

One important aspect of serving content with Caddy is that you can also support basic HTTP, and you can do that by accident. If you start your Caddyfile with a declaration of both 443 and 80 ports, your application will be served on both HTTP and HTTPS. If, however, you choose only the secured version, all HTTP connections will be automatically redirected.

Post scriptum

I realize that at the moment of writing this, MyCodeSmells.com still uses an insecure HTTP. Even though that it's just a simple blog and you don't send me any of your personal information, I would prefer to have the cool green lock icon in the address bar. The reason behind this is that I'm still using an old, shared hosting for my blog and I'm in the middle of switching into something more custom-made. Keep your fingers crossed for me finally moving there one day :)