Skip to content

Commit

Permalink
content: site migration to cloudflare
Browse files Browse the repository at this point in the history
  • Loading branch information
TrebledJ committed Nov 8, 2023
1 parent cfb71d3 commit 01ff8c2
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 18 deletions.
18 changes: 11 additions & 7 deletions _data/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,28 @@ const csp = require('./csp');
module.exports = function () {
const email = 'trebledjjj@gmail.com';

const banner = process.env.TARGET_HOST === 'github' ? {
const banner = {
enabled: true,
sticky: true,
closeButton: true,
scope: 'session', // Possible values: 'session', 'local', ''.
// bgColor: 'primary', // Any Bootstrap `bg-` values.
// fgColor: 'black', // Any Bootstrap `text-` values.
icon: 'rocket',
content: `
icon: 'rocket fa-bounce',
icon_style: '--fa-animation-delay: 5s; --fa-animation-duration: 3s',
/* eslint-disable max-len */
content: (process.env.TARGET_HOST === 'github' ? `
This site has buffed up and moved to
<a id="trebledjxyz" class="text-white" style="cursor:pointer;">***trebledj.xyz***</a>!!!
The .github.io version will stay around though... for now...
Check out what else is new: <a class="text-white" href="https://trebledj.xyz/posts/site-migration-to-cloudflare">***Site Updates and Migration***</a>.
<script>
document.getElementById('trebledjxyz').addEventListener('click', function () { document.location.host='trebledj.xyz'; });
</script>
`.trim().replace(/^[ \t]+/gm, ''),
} : {
enabled: false,
` : `
Welcome to the new site!
Check out what else is new: <a class="text-white" href="/posts/site-migration-to-cloudflare">***Site Updates and Migration***</a>.
`).trim().replace(/^[ \t]+/gm, ''),
/* eslint-enable max-len */
};

return {
Expand Down
2 changes: 1 addition & 1 deletion _includes/banner.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% if site.banner.enabled %}
<div id="banner" class="bg-{{ site.banner.bgColor | default('primary') }} collapsed">
<div id="banner-content" class="text-{{ site.banner.fgColor | default('white') }}">
<i class="fas fa-{{site.banner.icon}} fs-4 me-2"></i>
<i class="fas fa-{{site.banner.icon}} fs-4 me-2" style="{{ site.banner.icon_style }}"></i>
<span>{{site.banner.content | mdInline | safe}}</span>
</div>
{% if site.banner.closeButton %}
Expand Down
2 changes: 1 addition & 1 deletion assets/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ $carousel-transition: transform 2s ease, opacity 1.5s ease-out .5s;

#banner {
width: 100%;
padding: 0.5rem;
padding: 1rem;

display: flex;
flex-direction: row;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ tags:
- js
thumbnail_src: assets/ogle-ogle-eleventy.jpg
related:
disable: true
tags: [meta]
---

This post contains a brief explanation of this site’s migration and improvement over the last week.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
title: Site Updates and Migration to Cloudflare Pages
excerpt: Improvements, Optimisations, and a Better Stack with Cloudflare Hosting
tags:
- meta
- writeup
- web
thumbnail_src: assets/ogle-ogle-cloudflare-pages.jpg
related:
tags: [meta]
---

This is my second meta post on site development. I'll walk through some changes on the site, along with my assessment and decision-making process on migrating to Cloudflare.

But first things first. Meme.

{% images "h-auto" %}
{% image "assets/ogle-ogle-cloudflare-pages.jpg", "Ooh, Cloudflare pages looks pretty hot." %}
{% image "assets/yas-cache-policy.jpg", "One more item off the Lighthouse checklist!" %}
{% endimages %}


## What's new?

The [previous meta update](/posts/site-migration-to-eleventy) was dated February 4. That time, I revised the site generator, ditching the wavering framework known as Jekyll and moving to Eleventy. This enabled more rapid testing and development of new features, some of which are...

- Frontend
- Cooler UI(?) - Continuous
- Modern Web Elements ⚡️: Alerts, Details, Spoiler - May-September
- Image Lightbox (you can now click on images to *expand* them) - October
- Link Anchors 🔗 (hover the mouse to the left of headings) - October
- Content
- [Home Page](/) (uses carousels to cycle through content) - May
- [Privacy Policy](/privacy-policy) - September
- Assorted Blog Posts (including a [series on digital audio synthesis](audio-synthesis-for-dummies), [CTF writeups](/tags/ctf), and [4 new compositions](/tags/composition)) - Continuous
- Optimisations
- Lazy Loading (iframes, images, disqus) - May
- Images (responsiveness, etc.) - May
- JS/CSS Minification - September
- Migrate from MathJax to KaTeX - November (earlier this week!)
- Better Browser Caching for Assets - Now!
- Under the Hood
- Link Checking (with [Lychee](https://github.com/lycheeverse/lychee); so that you won't encounter broken links 😉) - June
- Linting (with [eslint](https://github.com/eslint/eslint)) - October
- Change in Domain Name - Now!

## Migrating Hosting to Cloudflare Pages

While the previous migration dealt with site generation, today's migration is twofold:
1. Migrating the hosting service from GitHub Pages to Cloudflare Pages
2. Migrating the domain name from `trebledj.github.io` to `trebledj.xyz`

To the point: why the switch? Plenty of users are content with GitHub Pages. Why am I not? Although perfectly suited for simple static sites, GitHub Pages lacks server-side flexibility and customisations.

### Analysis

To be fair, I've debated long and hard between GitHub Pages, Cloudflare Pages, and Netlify. These seem to be the most popular, most mature static site solutions.^[Vercel's also in the back of mind, but after running into login issues, I've decided that Netlify covers most of their features anyway.]

Anyhow, time for a quick comparison:

| | GitHub Pages | Cloudflare Pages | Netlify |
| --------------------------------------- | ------------------------------------------------------------------- | -------------------------------- | ----------------------- |
| Deploy from GitHub Repo | Yes | Yes | Yes |
| Domain Registration[^reg] | No | Yes | Yes |
| Custom Domain | Yes | Yes | Yes |
| Custom Headers | No | Yes | Yes |
| CI/CD | Free (public repos); 2000 action minutes/month ([private][1] repos) | 500 builds/month | 300 build minutes/month |
| Server-side Analytics | No | Yes (detailed analytics: \$\$\$) | Yes (\$\$\$) |
| Server-side Redirects[^notadealbreaker] | No | Yes | Yes |
| Serverless | No | Yes | Yes |
| Plugins | No | No | Yes |

See a more thorough comparison on [Bejamas](https://bejamas.io/compare/github-pages-vs-cloudflare-pages-vs-netlify/).

[1]: https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions

[^reg]: Note: Domain registration is done through a *DNS registrar* and usually requires money (~10USD/year). (You might be able to get cheap deals on NameCheap, sans security features.) Cloudflare and Netlify are DNS registrars. GitHub isn't. This isn't really a big deal. But it's more seamless to deploy your site and set a custom domain on the same service where you register your domain.
[^notadealbreaker]: This isn't really a deal breaker, but included for posterity.

Although Netlify and Cloudflare Pages are both strong contenders, I eventually chose Cloudflare Pages for its decent domain name price, analytics, its security-centric view, and a whole swath of other features.

For dynamic sites, Netlify wins hands down. Their plugin ecosystem is quite the gamechanger. But this also comes with the limitation that plugins may also be pricey (e.g. most database plugins come from third-party vendors, which *do* provide free tiers, albeit limited).

For static sites, Cloudflare Pages is optimal.

## Custom Headers: Caching

Custom headers can be used for different things. But one thing that stands out is browser-side caching.

{% alert "info" %}
When a browser loads content, it generally caches assets (images, JS, CSS) so that when the user *visits another page in your domain*, those assets are loaded directly from memory. As a result, network load is reduced and the page loads faster.

The server which delivers the files can tell your browser *how long the files should be cached*, using the `cache-control` header.
{% endalert %}

GitHub Pages tells our browser to only cache for 600 seconds (10 minutes). This applies to *all* files (both HTML documents and assets). On the other hand, Cloudflare Pages has longer defaults and offers more flexibility, especially for assets.

We can use `curl` to fetch headers and compare results. At the time of writing:

```sh
# GitHub Pages
curl -I https://trebledj.github.io/css/main.css | grep cache-control
cache-control: max-age=600

# Cloudflare Pages
curl -I https://trebledj.xyz/css/main.css | grep cache-control
cache-control: public, max-age=14400, must-revalidate
```

With Cloudflare Pages, our cache duration is longer (4 hours by default). Thanks to their flexibility, we can customise this further by...
- Changing the max-age through Cloudflare's Browser {% abbr "TTL", "time-to-live" %} setting.
- Enabling caching for other files (e.g. JSON search data).
- And more~

{% alert "warning" %}
**High Cache Duration**

Having a high cache duration may sound good, but there's one problem. Newer pages/content may not update immediately. The browser will need to wait for the cache to expire before fetching the content.

One way around this is to use **cache busting**. The idea is to use a different asset file name every time the contents are modified. This way, we can ensure a high cache duration (for assets) with zero "cache lag".
{% endalert %}

By caching static assets for a longer duration, we speed up subsequent page loads. And as a result, our Lighthouse Performance metric improves! (ceteris paribus)

### Sweet Server-Side Spectacles

As you may notice, GitHub Pages sorely lacks server-side customisations, focusing solely on the front-end experience. Let's talk about analytics.

#### Analytics

{% alert "warning" %}
Although Cloudflare Pages provides integrated server-side analytics, they are underwhelmingly rudimentary. Detailed analytics (e.g. who visit what page when) are [stashed behind a paywall](https://developers.cloudflare.com/analytics/faq/web-analytics/#can-i-see-server-side-analytics-by-url). So in theory, server-side analytics are great! In practice? 🤑🤑🤑.
{% endalert %}

**Server-side analytics** differ from client-side analytics, where the former relies on initial HTTP(S) requests to the server, and the latter relies on a JS beacon script. The two approaches differ drastically when we consider the performance impact. With server-side, the server does a (tiny) bit of additional processing. Usually, the IP address (`Host`), browser type (`User-Agent`), and referrer (`Referrer`) are already provided by the browser.^[Of course, this HTTP headers may be modified, e.g. by using a VPN.]

However, with client-side, the browser needs to load a separate script, adding to the network bandwidth. Some scripts are lightweight and simple. Some scripts may load a bunch of other scripts which hinder performance, whilst causing a privacy/compliance nightmare (looking at you Google Analytics).

GitHub Pages currently doesn't plan to support server-side analytics ([discussion](https://github.com/orgs/community/discussions/31474)). Cloudflare Pages does, but 💩🤑. It has a privacy-focused client-side solution though.

#### Serverless

Serverless is—lightly put—the hip and modern version of backends.^[Yes, "serverless" is a misnomer; ultimately, there are servers in the background. But the idea is that servers are abstracted away from the programmer.] Instead of having to deal with servers, load balancing, etc., we can focus on the functionality. Serverless lends itself well to the [JAMstack](https://jamstack.org/what-is-jamstack/) (JavaScript + APIs + Markup) approach to writing web apps.

Use cases include: dynamic websites, backend APIs, web apps, scheduled tasks, business logic, and [more](https://www.redhat.com/en/topics/cloud-native-apps/what-is-serverless#what-are-some-use-cases).

One reason for moving off GitHub Pages is to prepare for backend needs. I don't have an immediate use for serverless features yet; but if I do write web apps in the future, I wouldn't mind having a platform at the ready.

### Domain Stuff

Several reasons why I switched from `trebledj.github.io` to `trebledj.xyz`:

- **Decoupling**. I'd rather maintain my own {% abbr "apex domain", "domain name without any subdomains, generally the last two terms of the domain (e.g. github.io, microsoft.com, trebledj.xyz)" %} rather than rely on `github.io`.
- **Learning**. In the process, I get to touch DNS/networking settings, which are important from a developer and security perspective.
- **Personal Reasons**. Hosting this website on a custom domain name has been a mini-dream. Migrating to Cloudflare definitely eased the integration process between domain name and site.

### Considerations When Choosing a Domain Name

A few tips here.

1. **Choose a top-level domain (TLD) which represents your (personal) brand.** A TLD is the last part of your domain name. For example, in `trebledj.xyz`, the TLD is `.xyz`. Generally:
- `.com` for commercial use.
- `.org` for organisations.
- `.dev`, `.codes` for programmers/software-likes.
- There are thousands of TLDs to choose from, but I decided to choose `.xyz` because I like to do different things (not just spitting code), and I'm not a corporate entity. TBH, `.io` would've been my second choice.
2. **Check [domain name history](https://www.nameboy.com/how-to-check-domain-history/) for bad/good reputation.** It's possible the domain you're after was once a phishing site, but taken down. In any case, it's a good idea to check if the site garnered bad rep in the past.
3. **Avoid less reputable domains and [TLDs associated with cybercrime](https://circleid.com/posts/20230117-the-highest-threat-tlds-part-2).**[^xyz] (.date, .quest, .bid are among TLDs with the highest rate of malicious activity.)
4. **Choose a domain name provider/registrar.** GoDaddy, Domain.com, Namecheap are some well-known ones. They offer discounts, but security features may come with a premium. Cloudflare has security batteries included, and afaik they don't charge a premium? (They don't have first-year discounts though.)

[^xyz]: .xyz appears in the [list of most malicious/phishing TLDs](https://www.cybercrimeinfocenter.org/phishing-activity-in-tlds-november-january-2023). Does that mean I shouldn't have used it? The problem with that metric is that .com, .org, and .net are also in those lists. And depending on how you sort the lists, some may appear on top. For example, .com has the most number of phishing domains: does that mean we shouldn't use domains from .com? Nah. Even .xyz is used by reputable companies such as abc.xyz (Google's Alphabet Inc.) or starship.xyz (made by the founders of Skype). More examples can be found in [gen.xyz](gen.xyz), a site for registering and viewing examples of .xyz domains.

Further Reading:
- [Palo Alto: TLD Cybercrime](https://unit42.paloaltonetworks.com/top-level-domains-cybercrime/) - Comprehensive analysis of malicious sites, if you're into security and statistics.

## Closing Thoughts

Overall, I decided to migrate to Cloudflare Pages to improve the site's performance and to future-proof it, by having greater control over the backend.

Some more work still needs to be done, though.

- Setting up 301 redirects from the old site, for convenient redirect and migration of site traffic.
- Implement longer browser cache TTL + cache busting.

But other than that I'm quite happy with Cloudflare's ease-of-use, despite its day-long downtime (which coincidentally occurred when I was setting up Cloudflare Pages).
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions eleventy/detail/related.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ function getRelatedPosts(posts, tags, related) {

// Find relevant posts with same tags as `related.tags`.
if (related.tags) {
if (!Array.isArray(related.tags))
throw new Error("getRelatedPosts: expected array");

for (const post of posts) {
if (finalRelated.size >= numTargetPosts)
break;
Expand Down
21 changes: 13 additions & 8 deletions eleventy/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,19 @@ module.exports = function (eleventyConfig) {
return html.replace(dumbHTMLRegex(tags), '');
return tags.reduce((acc, x) => acc.replace(dumbHTMLRegex(x), ''), html);
});
eleventyConfig.addFilter('annihilate', (html, selector) => {
if (typeof html !== 'string') {
throw new Error(`[11ty] annihilate: expected HTML string, got ${typeof html}`);
}
const $ = cheerio.load(html);
$(selector).remove();
return $.html();
});
if (process.env.ENVIRONMENT === 'fast') {
// Fast: Do nothing.
eleventyConfig.addFilter('annihilate', (html, _selector) => html);
} else {
eleventyConfig.addFilter('annihilate', (html, selector) => {
if (typeof html !== 'string') {
throw new Error(`[11ty] annihilate: expected HTML string, got ${typeof html}`);
}
const $ = cheerio.load(html);
$(selector).remove();
return $.html();
});
}

// Get the first `n` elements of a collection.
eleventyConfig.addFilter('head', (array, n) => {
Expand Down

0 comments on commit 01ff8c2

Please sign in to comment.