Skip to content

Instantly share code, notes, and snippets.

@amingilani
Last active October 21, 2021 18:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amingilani/332f3b5102f65a63719b2b4578b50108 to your computer and use it in GitHub Desktop.
Save amingilani/332f3b5102f65a63719b2b4578b50108 to your computer and use it in GitHub Desktop.
gh pages with https

Always Available SSL Websites at $0

I have a secret that saves my clients a ton of money in the long run, keeps their website secure and has built in backups. I make their website static. I, then, store and host it with Github, and I finally use Cloudflare to serve it over HTTPS, and make it fast. My clients only ever pay for their domain name.

[TOC]

Why Static Content?

Static sites are wonderfully fast since there's no server processing times involved. Also, by committing a code base of static assets in a git repository, rolling back changes simply becomes a matter of reverting to a previous commit. Backups are a git push away, and you essentially serve your whole website from cache, meaning your server would almost never have to process a request again.

Building a complex user-interface? with the advent of front-end frameworks like React and its kin you can create magical experiences with nothing more than HTML/CSS and Javascript. You'll have to separate your backend logic from your frontend though, but even Ruby on Rails ships with an API mode now.

Whenever I get contracted to build a website, I consider if a static site is enough meet my client's needs, and it usually does. Let's discuss some situations where you should consider static content, and why.

Brochureware websites

Brochureware websites are meant to provide information about a business and don't change significantly throughout their life. A dynamic application is clearly overkill for such sites, and since these sites go unmaintained without updates for years, they're ripe targets for hackers to break into.

Static HTML templates are also significantly cheaper than their CMS counterparts and they are easier to tweak for future developers because they don't require specialized knowledge about a CMS. As a rule, I always make static websites for brochureware sites.

Bonus: since static site hosting is free; small business clients love not having to pay recurring hosting fees!

Single Page Applications

Are you showing off a wonderfully cool new app that uses modern frontend frameworks? Your application is already mostly static! Take a few extra moments to isolate any server side logic into a separate application and get the full benefit of having your app served entirely from Cloudflare's cache.

Your application will be always available, even if the actual server fails!

Blogs

This can be a little tough to convince people about, but hear me out. Blogs are nothing more than content rendered with templates. You don't need a full blown application parsing each request and rendering a new page. A static site generate is perfect for this use case.

Consider Jekyll: you give it Liquid templates and Markdown content, and it combines them together into a static website. No on the fly processing required, and your blog suddenly feels significantly faster!

This workflow is especially helpful because Github pages supports Jekyll builds. Suddenly blog posts can be contributed with pull requests and all your content is stored within version control. Non developers can still contribute posts in Markdown by publishing their posts through Stackedit. Infact, I'm using Stackedit to compose this post right now!

Github Pages

Github Pages is Github's answer to project project pages, and it allows you to serve any static website straight from your repository. Since Github pages allows custom domains, you can host a static website on Github pages, for free and with deploys straight from Git.

Deploying to Github Pages

Enough talk, let's see this in action. I've gone ahead and made a single page React app that fetches and displays the current exchange rate for the Pakistani Rupee from a public API. Let's deploy this to Github Pages. First let's create a new Github repository.

New Github Repo

Github pages are served from a branch called gh-pages so let's create one for my project.

$ git checkout -b gh-pages
Switched to a new branch 'gh-pages'

And let's push the site up:

$ git remote add origin git@github.com:amingilani/price-check.git
$ git push -u origin gh-pages
Counting objects: 27, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (25/25), done.
Writing objects: 100% (27/27), 28.67 KiB | 0 bytes/s, done.
Total 27 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), done.
To github.com:amingilani/price-check.git
 * [new branch]      gh-pages -> gh-pages

And we're done! At this point the website will be available at https://amingilani.github.io/price-check with free SSL:

Site hosted on Github pages

Important things to note:

  • Github pages serves the index.html file in your project's gh-pages branch
  • The website is served at USERNAME.github.io/REPOSITORY-NAME

Customizing the Domain Name

Serving the site is fine but any decent website needs a custom domain name. Luckily, Github let's you Bring Your Own Domain (BYOD?) to the party!

First, let's create a special CNAME file and place our domain name there. This will let Github know which domain name to route to the repository!

$ echo 'pricecheck.gilani.me' > CNAME
$ git add .
$ git commit -m 'Add a custom domain'
...
$ git push
...

Second, let's point a CNAME for our subdomain to Github's DNS at USERNAME.github.io:

Point a CNAME to our domain

Caution: Do NOT use this for an apex domain. Adding a CNAME record to the root your domain will disable your MX and TXT records. Use this only for your subdomain. Apex domains are discussed later.

At this point, our website should run on our custom domain on HTTP:

Configured to use a custom domain

Important things to note:

  • The default *.github.io domain is served through HTTPS
  • Our custom domain name is served through insecure HTTP
  • Do NOT use a CNAME record on your apex domain unless you want to kill your emails.

[Limitations][ghpages] of Github Pages:

Using an apex domain as your custom domain

The easy way to get around this limitation is to use www as your subdomain and redirect all HTTP traffic from the apex to www, e.g. in my example, I would redirect gilani.me to www.gilani.me, which point to my static site, but I don't like doing things the easy way.

If you really want to use an apex domain, check if your DNS provider lets you set ANAME records. These are (simplified) halfway between of CNAME records since they let you point to domains, and A records since they don't nullify other records on the same zone.

No ANAME? The last option is to change to a DNS provider that does support this: enter Cloudflare. Cloudflare provides CNAME flattening on apex domains, which is the equivalent of an ANAME record. It's best to make the switch right now, since we'll be covering Cloudflare in the next section.

TLDR: Switch to Cloudflare's free DNS and set a CNAME on your apex domain. They do something special with their CNAME that makes it work.

SSL & Cloudflare

Welcome to the post-Snowden era. All our worst fears of state level spying and hacking have been confirmed and the world is scrambling to secure everything.

Encrypt all the things

As a modern web admin, you're expected to provide at least SSL on your website, with no mixed content. It's come to the point that Google Chrome marks plain HTTPS websites are insecure and Google Search is beginning to favor HTTPS websites favorably in their rankings. We'll discuss even further hardening strategies latter, but for now, you we'll only cover SSL.

Fortunately we now have Let's Encrypt. It's a non-profit, and entirely automated Certificate Authority (CA) that lets you programmatically issue short-term 90-day SSL certificates for any domains you control. It's a breeze to use, it's open source and the project is backed by a plethora of companies including Mozilla and the Electronic Frontier Foundation.

Cloudflare

Cloudflare is a DNS, CDN and DDoS protection service. It caches your website and serves it to users from servers geographically close by, making your website faster. This has the added benefit of keeping you under Github's 100 GB bandwidth limit, because even if your website becomes insanely popular, most requests will hit the cache, and never reach the server. On top of this, Cloudflare offers a service called Universal SSL where they issue you a free SSL certificate from their CA partners, so you get HTTPS for free!

Why Cloudflare

I know what you're thinking: Gilani, you just told me how awesome Let's Encrypt is. Why are you talking about Cloudflare? Well, just one reason: simplicity.

As a mental exercise, imagine setting up multiple Nginx cache and reverse proxies around the world, giving them all valid SSL certificates, and serving users web pages from their closest locations. This results in your website being served via SSL even if the origin server doesn't have an SSL certificate.

This is what Cloudflare gives you with a free plan, and you don't even have to renew your certificate every 90 days.

As a freelancer I get clients that want a site up and running for their business. They don't understand or care about the security of the modern web, or encryption during transit. Some clients struggle to understand the idea of domain names, and find it unexpected and annoying that they have to pay a yearly $15 fee "just to keep my website running". Try explaining to them why they have to pay for a cluster of reverse proxies protecting their website that runs on free hosting.

Setting up Cloudflare's SSL

Let's get our hands dirty again. First thing to do, switch to routing all your traffic through Cloudflare:

DNS and proxy

Next, under Crypto, set the SSL level to "Full":

Switch crypto to full

Force "Automatic HTTPS Rewrite" to kill mixed content warnings:

Enable Automatic HTTPS Rewrite

At this point, our website will work over both HTTP, and HTTPS. Let's force HTTPS for everything on our domain:

HTTPS everywhere

All done, our website should always load over HTTPS with a green "Secure" on Chrome:

enter image description here

Final Words

There are a few things I didn't discuss above, I'd like to take a moment to clarify.

Security Considerations

The astute among you will point out that there are a few glaring security problems with this setup, namely that there are no secure HTTP headers like:

And you're right. Github pages and even Cloudflare does not allow you to customize your HTTP headers. However you can set a CSP using the HTML meta tag. Simple insert this into your web page:

<meta http-equiv="Content-Security-Policy" content="default-src https:">

However at the moment, there is no way to set the X-Frame-Options header on Github pages, meaning an attacker can load your webpage into a specially crafted iframe and pull off an XSS attack. If you're dedicated, though, you can workaround this issue by asking users to confirm their password or 2FA token upon every sensitive action, or by passing a CSRF token upon every authenticated request.

Confession & Links

During this article, I made it appear as though I manually published my react app to gh-pages. I didn't. I worked on master and when it came time to deploy, I ran npm run deploy which kicked off a build script and pushed the build to gh-pages. Please see the master branch of my repository to see how it works.

Links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment