Build Faster Websites with Craft CMS Template Caching

Reducing database queries

Just as you can cache static assets (images, JavaScript, CSS, etc.), you can cache the HTML output itself, or chunks of the HTML output. This is particularly useful when a portion of your Twig template relies on multiple database queries to render.

Instead of requesting content from the database every time the page loads, you can store the resultant HTML output once and simply fetch it on subsequent page loads.

Let's start with an example

The average blog probably has a "Related Blog Posts" section. This usually contains the titles and blurbs of three additional blog posts within the same category.

A few database queries are required to retrieve this content. You'd probably write something like this:

{% set relatedPosts = craft.entries.section("blog").relatedTo(category).limit(3) %}

This will ask Craft CMS for the content and Craft will generate all of the necessary database queries behind the scenes to pull everything together.

Without any template caching, these database queries are going to happen every time the page is loaded, even though the content is probably going to be the same for a while.

This makes your server work harder and it increases the time it takes the page to load for every visitor. And this is just one section of the page. As you can imagine, a very complex or content-heavy page will probably require many more database queries to render everything.

Caching a section of the page

Let's cache this "Related Blog Posts" section.

{% cache using key "related-posts" %}

  {% set relatedPosts = craft.entries.section("blog").relatedTo(category).limit(3) %}

  <div class="related-posts">

    <h2>Related Blog Posts</h2>

    {% for post in relatedPosts.all() %}
      <h3>{{ post.title }}</h3>
      <p>{{ post.blurb }}</p>
    {% endfor %}

  </div>

{% endcache %}

Using the {% cache %} tag, Craft CMS will save the final HTML output (after all database queries) as a blob in the database. The next time the page is loaded, it just needs to fetch this blob of HTML rather than all of the content required to create the HTML newly.

Note: If you don't specify a key, one will be automatically generated for you, but naming them makes it easier to see what's going on in the database.

Caching the whole page

Sometimes, depending on the content, you will want to cache the entire page as one chunk of HTML. Just make sure you're caching the content of the page, not the entire Twig template.

As the Craft CMS docs demonstrate:

{# Bad: #}

{% extends "_layout" %}
{% cache %}
  {% block content %}
    ...
  {% endblock %}
{% endcache %}

{# Good: #}

{% extends "_layout" %}
{% block content %}
  {% cache %}
    ...
  {% endcache %}
{% endblock %}

Caching elements globally

By default, content is only cached for the page it appears on. In our "Related Blog Posts" example, that HTML will be cached uniquely for each blog post, which makes sense because each blog post will have different related posts.

But sometimes it makes sense to cache a portion of the template globally. A great example of this might be the website's footer.

If the footer is the same on every page, we should cache it globally so only one blob of HTML is stored. If we don't cache it globally, the same footer HTML will be cached in the database for every single page of the website, which kind of defeats the purpose.

{% cache globally using key "footer" %}

  <footer>
    {% for link in footer.links.all() %}
      <a href="{{ link.url }}">{{ link.title }}</a>
    {% endfor %}
  </footer>

{% endcache %}

A word of warning

Be careful not to globally cache content that needs to be dynamic. For example, the navigation might have active states depending on which section of the website you're in. This can be frustrating to debug if you've forgotten it's being cached globally.

Performance gains

Using my own website's home page as an example, let's look at the performance before and after template caching.

Before

Page Load Time: 942 ms
Database Queries:
65

After

Page Load Time: 662 ms
Database Queries:
18

Results

The overall page load time was reduced by 280 ms and the number of database queries were reduced by 47!

While this is a very nice improvement, my home page is fairly lightweight to begin with. You can expect a much greater performance increase for more complex layouts and content-heavy pages.

Don't cache static HTML

Don't cache HTML if it doesn't perform any content queries.

Doing so causes additional unnecessary database queries, because caching the static HTML will store it in the database and require it to be fetched from the database later, rather than just rendering.

<div>
  <p>This is just static HTML. It doesn't load any content.</p>
</div>

Read the docs!

Check out the official {% cache %} tag docs to learn about additional options and best practices.

Ready to discuss your project?

I help agencies, marketing teams, and founders get their projects out of design documents and into web browsers.

Start a conversation