skip navigation
"Software developer with a kaizen mindset"

How I host my static website

Thursday Apr 29, 2021 | updated Dec 9, 2021

I recently changed how I hosted my blog as my last solution turned out not very stable. As my site isn’t exactly a moneymaker it had to be a low cost solution with some security standards. I’m still a responsible developer after all or so I like to think.

So I had some requirements:

  • Add all the security headers that are recommended by securityheaders.com
  • Have SSL as it is 2021
  • It needs to be stable
  • Have both the naked domain and the www point to the same content

Hosting and deployment

So first I needed my content hosted somewhere. For a bit of background: I use gohugo as my static site generator and up to this point I have a gitlab instance hosted on my k8s cluster as my source control.

There are quite a few ways to host a static website and I needed to get to a short list. I considered going for a standard shared hosting solution at first, but I’d be paying for things I wouldn’t use as most of come with domains and mail hosting. Both I already have configured centrally for all my domains.

So I looked at cloudflare pages and netlify for hosting. Both offer free plans with limits on traffic and such, both limits well above the traffic I’m getting. I had some bias towards cloudflare primarily because I was already using them for dns and quite like their dashboard. However netlify offers the security headers support out of the box. I ended up find a solution for the headers as part of cloudflare, so ended up going with them. Mostly to keep things simple and centralized.

I configured the cloudflare pages instance with my custom domain using www.haukerolf.net. Yes, with a subdomain www.

Flow so far: Github repository > Cloudflare pages > www.haukerolf.net

Pipeline

Cloudflare pages supports numerous static file generators and even custom commands. Although I have obviously only tried those for gohugo. Cloudflare pages only integrates with github and I kind of hestitated to have my site there publicly. Luckily these days github supports unlimited private repositories so that was one worry less.

Still my preference is to only have to connect to a single git location on my dev machine. The nice thing is that gitlab supports mirroring your repositories to the outside. See here for details.

So I setup mirroring from my gitlab instance to the github private repository. I made sure to only mirror protected branches this way not every push locally would end up as a preview version in github pages. For now i’m only mirroring and thus publishing my main branch and have a development branch to push to from my machine.

To make sure i’m not wasting my build credits at cloudflare I’ve setup gitlab-ci to build my static site on each merge request. And i’ve prevented myself from pushing directly to the main branch.

Flow so far: Local git repo > Gitlab dev branch > MR to gitlab main branch > Github repository > Cloudflare pages > www.haukerolf.net

Security headers

Now I’m up and running and can release new posts with just a few clicks. To be honest the merge requests takes the most “effort” if you can call it that. Next we need to up the security a bit as SSL is provided out of the box, but we have not set our security headers yet.

This can be done by adding a cloudflare worker that intercepts requests and adds headers on the fly. There are already many blog posts about how to do it I used the one by Scott Helm and the code on the corresponding github repo.

Note: since this blog post cloudflare has added a free tier to the workers. At the time of writing the first 100.000 requests are free. Though this is from what I can tell the first part of this setup that will start costing money when traffic increases.

(Edit 9 Dec 2021) Note: as of now cloudflare offers a new feature to define headers for cloudflare pages in a _headers file included in the root of your deployed site. Read more in their documentation

Flow so far: Local git repo > Gitlab dev branch > MR to gitlab main branch > Github repository > Cloudflare pages > Cloudflare worker > www.haukerolf.net

Naked domain support

Now all I needed was naked domain support to make it a bit more userfriendly to navigate to my site. This was a simple redirecting page rule. First I needed to make sure that the naked domain was proxied by cloudflare, for this I made use of the cname flattening feature of cloudflare and just point it to my www.haukerolf.net subdomain.

Then I configured the redirecting page rule as per their support page

Flow so far: Local git repo > Gitlab dev branch > MR to gitlab main branch > Github repository > Cloudflare pages > Cloudflare worker > www.haukerolf.net < haukerolf.net (redirect)

SEO

Now a small note on SEO, though I’m far from an expert. I made sure to include the following meta tag, so search engines know what the canonical url is and prevent marking my domains as containing duplicate content.

<link rel="canonical" href="{{ .Permalink }}" itemprop="url" />

It may not be strictly necessary as i’m doing 301 redirects from my naked domain. Still it is a nice to have incase the redirects don’t work and it is using the cname flattening as per the DNS entries.

Now the flow is complete and unless my traffic grows by a lot, it is completely free.

Alternatives

These components allow me to swap out parts of the flow for other components. For example for more static static sites (i.e. those that don’t really change much) I can and have swapped cloudflare pages with a GCS bucket. Having this in mind also made me make sure the canonical url and actual hosting was configured for the www subdomain.

The complete solution

So now I can push changes from my local git to my gitlab dev branch. Then check my changes in a MR and automated build when merging to my gitlab main branch which in turn is mirrored automatically on merge/push to my github private repo. These mirrored pushes automatically trigger a build and deploy on cloudflare pages. Making it available on my www subdomain and naked domain with security (ssl and headers) provided by cloudflare pages/workes/page rules.

Share on:
Support: