Appearance
Forms and CSRF
Craft protects forms with a CSRF token, unique to each visitor. A cached page can’t contain one visitor’s token, or everyone would share it and forms would fail.
Speedy handles this for you:
- When a page with a CSRF token is cached, the token is replaced with a placeholder.
- A tiny script (under 1 KB, no dependencies) is added to the end of the page.
- When the page loads, the script fetches a fresh token from
actions/speedy/csrf, which is never cached, and puts it into every CSRF field and meta tag.
Your templates don’t change:
twig
<form method="post">
{{ csrfInput() }}
{{ actionInput('contact-form/send') }}
…
</form>Pages without forms get no script.
Scripts that read the token
The script also sets window.csrfTokenValue once the token arrives. If your own JavaScript reads the token, read it when the form is submitted, not as the page loads.
Craft’s {{ csrfInput({ async: true }) }} works too: Craft fetches its own token, and Speedy leaves it alone.
Turning it off
Set refreshCsrf to false in config/speedy.php. Pages with CSRF tokens are then not cached, as Craft marks them no-store.
Formie
Formie keeps each visitor’s progress through a form, and its “Thanks” message, on the server, linked to the visitor’s session. So:
- Pages that show a Formie form aren’t cached (
X-Speedy: SKIP session). A cached copy could show one visitor’s step or message to someone else. - Seeing a form doesn’t affect other pages. The visitor still gets cached copies of every other page.
- After submitting, the visitor gets fresh pages until the confirmation has been shown.
To cache a page that has a form on it, such as a newsletter sign-up in the footer, include the form with craft.speedy.dynamic(). The page is cached, and the form, its steps and its messages are built for each visitor:
twig
{# In the footer #}
{{ craft.speedy.dynamic('_partials/newsletter') }}twig
{# templates/_partials/newsletter.twig #}
{{ craft.formie.renderForm('newsletter') }}Formie’s scripts and styles come with the form, and its validation and multi-page steps work as usual. See Personal content on a cached page.