HTTPS for your Github Pages' custom domain
With the rise of free encryption through Let’s Encrypt, and the weight of global surveillance on our minds, adopting HTTPS is now more important than ever. Github now allows unforced HTTPS for its username.github.io
domains, but that coverage doesn’t carry over to those using custom domains. The approach I’m using for this blog and my home page, by way of reverse proxy, is documented herein.
Github behavior change (May 2018)
Github now officially supports custom domains using HTTPS, due to a partnership made with Let’s Encrypt. As such, this approach is no longer necessary, but can be useful for those who want more control over how the site is served (such as TTLs, permissions, etc). See the relevant Github blog post here.
Github behavior change (May 2016)
Github has “fixed a bug” which now causes issues with this setup, since the presence of the CNAME
file will redirect to your custom domain, but your custom domain will act as a reverse proxy to the github.io
address, thus causing an infinite loop. The simple solution is to delete the CNAME
file in your repo when you set this up; it’s not needed with this approach.
SSL Certs
Before any HTTPS configuration can be setup, SSL certs are required. I use simp_le, which is a Let’s Encrypt front-end, and NixOS (related configs); there are many others to consider though. Let’s assume you’re building your new website honest-kittens.org
. Be sure to include at least www.honest-kittens.org
in your certificate as well.
The Apache proxy
The way to get around Github’s lack of SSL support for custom domains is to have that domain use a proxy server which talks to Github and the client. This isn’t very much work, compared to hosting a complete Jekyll stack, so we still benefit from Github’s convenient hosting. To minimize the scope of this post, I assume you’re familiar with administrating an Apache server.
You’ll need to load the proxy
and proxy_http
modules before anything. Apply the following, updating the paths as needed:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
In the virtual host for honest-kittens.org
, we can enable the proxy engine using:
SSLProxyEngine On
ProxyPreserveHost Off
This will also ensure that, when talking to Github, the proxy server doesn’t use the honest-kittens.org
host. If it did, the SSL discussion would fail. To have Apache relay honest-kittens.org/
to username.github.io/
, where username
is your username, we setup a proxy pass:
ProxyPass / https://username.github.io/
NOTE: My placement of trailing slashes is deliberate and extremely important, as is my usage of https vs http.
Github’s server may, for various reasons, send over a 301 redirect request. With just this configuration, the request will be forwarded to the client unmodified, causing them to end up at username.github.io
. That’s no good. We’ll setup a reverse proxy to ensure such a redirect from Github is changed before it hits the client:
ProxyPassReverse / https://username.github.io/
ProxyPassReverse / http://username.github.io/
This covers both the HTTP and HTTPS cases, ensuring that links matching the above username.github.io
will be translated into the root level of honest-kittens.org
. The only additional configuration necessary is for bringing in the SSL certs and ensuring no weak ciphers are used:
SSLCertificateKeyFile /path/to/honest-kittens.org/key.pem
SSLCertificateChainFile /path/to/honest-kittens.org/chain.pem
SSLCertificateFile /path/to/honest-kittens.org/cert.pem
SSLProtocol All -SSLv2 -SSLv3
SSLCipherSuite HIGH:!aNULL:!MD5:!EXP
SSLHonorCipherOrder on
Updating your Jekyll configuration
At this point, our Github Pages site needs one tweak before all of this will work together. We could use mod_proxy_html to rewrite all of the references to our username.github.io
site, within the HTML, or we could just change our _config.yml
, and the like, to be aware of our custom domain. The choice is yours, but I’ll describe the latter.
site: https://honest-kittens.org
After setting the site’s url in Jekyll’s configuration, we should use it for all file references within our site. That is, our main.css
might come in as:
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/main.css" />
The {{ site.url }}
is a Liquid expression which will be replaced by the value in your configuration.
Forcing HTTPS
Now that your reverse proxy is setup, it’s crucial that you force your users onto HTTPS and keep them there. This article covers how to do that simply using Apache. Since I’m using NixOS, I can just specify a global redirect:
services.httpd.virtualHosts =
[
{
hostName = "honest-kittens.org";
serverAliases = [ "www.honest-kittens.org" ];
globalRedirect = "https://honest-kittens.org/";
}
];
Note that it’s also important to add a server alias for www.honest-kittens.org
in both your HTTP redirect and your SSL-enabled virtual host.
The Let’s Encrypt effect
According to a very recent post by J.C. Jones, Let’s Encrypt is now the fourth largest CA for public web certs. Even more interesting, 93% of all sites using Let’s Encrypt didn’t have a previous SSL certificate; this means more and more new sites, or previously unencrypted sites, are picking up encryption for free.