Skip to main content

Caching FormsImproved in 5.0+

Forms and pages can be cached in a variety of ways. Here are 2 different solutions (plus a guide for caching Stripe forms), depending on your caching approach:

Twig Template CachingRevised in 5.0+

When using simple Craft Caching, you'll need to make sure that you are refreshing the CSRF token.

There are two approaches you can take:

Using Craft's asyncCsrfInputs SettingRecommended

The simplest approach you can take is enabling Craft's asyncCsrfInputs setting. It will automatically take care of the CSRF token for you and you don't need to do anything else.

{# Initialize the form #}
{% set form = freeform.form("myFormHandle") %}

{# Cached form comes here #}
{% cache %}
{{ form.render }}
{% endcache %}

Manually Refresh CSRF in Template

If you need wish to refresh the CSRF token manually instead, this can be done with a but of javascript in the template:

{# Initialize the form #}
{% set form = freeform.form("myFormHandle") %}

{# Cached form comes here #}
{% cache %}
{{ form.render }}
{% endcache %}

{# Script for updating the form's CSRF token #}
<script>
// Find the corresponding Form
var form = document.querySelector('form');

// Locate and update the CSRF input
var csrfInput = form.querySelector('input[name={{ craft.app.config.general.csrfTokenName|e('js') }}]');
csrfInput.value = '{{ craft.app.request.csrfToken|e('js') }}';
</script>

Static Page Caching / CDN / BlitzRevised in 5.0+

Services like CloudFlare allow you to cache components of web pages like JS, CSS and images. But you can also cache entire web pages; the complete HTML of a page. They then distribute these cached copies of pages across their global network of data centers. This makes websites screaming fast. The Blitz Craft plugin is another way for static page caching.

There's one problem with full page caching though. Forms stop working after a while since their CSRF tokens expire. CSRF tokens are a way of cutting down on unwanted form submissions and assure that a given form actually belongs to a given website.

You can overcome this problem of expired CSRF tokens in Craft CMS with some simple javascript and a little Twig template code.

1

Create a separate Twig template to handle loading the refreshed tokens with the following code:

{{ {
csrf: {
name: craft.app.config.general.csrfTokenName,
value: craft.app.request.csrfToken,
}
}|json_encode|raw }}
2

Make sure routing to that template works correctly. For example, we often dedicate a directory inside the Craft templates directory named something like dynamic on our websites and place files in that directory that we know should never be cached by the CDN. The above template might be in a directory called dynamic with a filename of index.twig.

3

Set up a rule in CloudFlare or your preferred CDN that makes sure any URLs starting with dynamic are not cached. You can then aggressively cache all of your other site URLs.

4

Add the following JS snippet to your main .js file or to the bottom of all your web pages. Note that this snippet assumes you're already running jQuery. It uses AJAX to fetch the contents of https://yourwebsite.com/dynamic, which just returns a fresh CSRF token (and anything else you include). This is then replaced in the CSRF token field of all forms on the page.

{# Initialize the form #}
{% set form = freeform.form("myFormHandle") %}
{{ form.render }}

{# Load the jQuery library #}
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>

{# The script for updating the form's CSRF token loads after #}
<script>
$(function () {
// Find the corresponding Form
var form = document.querySelector('form');

$.ajax({
// Specify the form handle in the GET parameters
// ! Make sure to change the `myFormHandle` to your specific form handle.
url: 'https://yourwebsite.com/dynamic',
type: 'get',
dataType: 'json',
success: function (response) {
// Locate and update the CSRF input
var csrf = response.csrf;
form.querySelector('input[name=' + csrf.name + ']').value = csrf.value;
},
});
});
</script>
Finished!

Stripe Integrations

When using the built-in Stripe integration, Freeform will trigger Payment Intent requests automatically upon page load. These requests use the initial form data, meaning the original CSRF token.

The assumption is that payment forms should never be cached, so if your form has a Stripe integration enabled and you are using Blitz caching and refreshing your CSRF tokens, we suggest injecting your payment form into your template using Blitz dynamic content or Sprig.

There is then no need to refresh CSRF tokens as the dynamic content is injected after the page loads. Otherwise, you risk a 400 Bad Request - Unable to verify your data submission error.