5 seconds to 200 ms — Durable Caching for Astro Server Pages

Jason LengstorfJason Lengstorf

I'm working on a new version of the Learn With Jason site and taking advantage of Astro 5 + the server islands features.

A new feature on the site is profiles for both guests and supporters of the show, which allows them to update their links, bio, and profile photo.

I've had hundreds of guests on my various series, and — if all goes well — we'll have hundreds of supporters on the site as well. Keeping everyone's data up-to-date is really important. Running a full site build adds lag (and lots of extra builds), but making all the API requests on every page load could risk hitting rate limits or bandwidth issues in addition to adding unnecessary load time.

So we're going to try and have our cake and eat it, too: we'll load user profiles with server rendering, but we'll use Netlify's durable caching headers to minimize the burden on our APIs and speed up page loads.

I first saw this technique on Bryan Robinson's YouTube — thanks Bryan for putting that together!

Cache server-rendered pages in Astro

The good news is that it's a three-step process to add durable caching to your Astro site.

First, install the cdn-cache-control package to make setting the headers less of a memorization exercise.

Terminal window
npm i cdn-cache-control

Second, add CDN=netlify to your environment to let cdn-cache-control we're using Netlify so it can send platform-specific headers:

.env
CDN=netlify

Third, import the CacheHeaders class in your Astro page or component and create an instance with the settings you want, and pass that to Astro's header helper:

src/pages/profiles/[username].astro
---
import { CacheHeaders } from 'cdn-cache-control';
// ... skipping unrelated code ...
const headers = new CacheHeaders()
.swr()
.ttl(86_400)
.tag(['profile', `profile-${username}`]);
headers.forEach((value, key) => {
Astro.response.headers.set(key, value);
});
// ... skipping unrelated code ...
---
<Layout>
...

When we create a new instance of CacheHeaders, we then chain a few methods to configure our cache:

  • swr — this enables stale-while-revalidate, which means that the CDN should serve cached content after it's expired and fetch new content in the background to ensure no one has to deal with a slow load
  • ttl — this tells the CDN how long the page should be cached (in this case 1 day, or 86,400 seconds)
  • tag — this adds cache tags, which allow us to invalidate the cache before the ttl timeout

Once the headers are created, we loop through them and use the Astro built-in helper for headers to add them to the response.

Validate that caching is working as expected

After deploying — don't forget to set the CDN env var in your production environment as well! — open your dev tools on the Network tab, then visit the URL of the page you added caching to.

Look at the page request (filter to "Doc" to find it) and look at the response headers. If all has gone well, on the first request to the page you'll see "Netlify Durable" in the Cache-Status header, similar to the below screenshot:

Astro + Netlify durable cache, first request showing a durable cache miss

This is the request that populates the durable cache. Afterward, every request within the TTL limit will show a cache hit from either the durable cache or the Netlify Edge network, similar to the screenshots below:

Astro + Netlify durable cache, second request showing a durable cache hit

Astro + Netlify durable cache, third request showing a Netlify edge cache hit

In the case of profile pages on Learn With Jason, loading all the data from zero can take around 5 seconds. By adding these cache headers, profile loads dropped to around 200–300ms, and I'm able to invalidate a single profile page when it changes using cache tags.

If you want to see the full code I implemented on the new Learn With Jason site, check out the source on GitHub.

Give durable caching a try on your server-rendered Astro pages — post the before and after for loading times!

Get the latest Astro news, tutorials, and updates delivered to your inbox.

I respect your privacy. Unsubscribe at any time.