Enabling CSRF Protection

Craft has built-in, automatically-enabled protection against Cross-Site Request Forgery (CSRF) attacks.

Any time you generate a CSRF token for a user, Craft sets a CRAFT_CSRF_TOKEN validation cookie on their browser. Subsequent POST requests must be accompanied by a matching token—if they aren’t, Craft rejects the request with a 400-level error.

In most cases, Craft handles both halves of this with a single change to your forms. You can also support CSRF over Ajax!

CSRF in HTML Forms #

Any <form> that uses method="post" must include a CSRF input. Craft comes with a csrfInput() function that generates a complete, hidden HTML input element:

<form method="post">
  {{ csrfInput() }}

  {# ... #}
</form>

This is typically equivalent to manually constructing an input:

<input
  type="hidden"
  name="{{ craft.app.config.general.csrfTokenName }}"
  value="{{ craft.app.request.csrfToken }}">

Caching and Asynchronous CSRF #

Caching CSRF tokens can lead to bugs and security issues. Never manually output a token inside a {% cache %} tag, and avoid statically caching pages with forms that rely on CSRF.

However, Craft 5.1 added support for cachable, “asynchronous” CSRF inputs. As long as you are using the csrfInput() function in your templates, you can enable the asyncCsrfInputs setting to replace its output with a cachable placeholder element. Craft registers a JavaScript snippet that fetches a CSRF token over Ajax and injects a hidden input—as though it was there all along.

You can strategically opt-in to asynchronous CSRF by passing an async key to the csrfInput() function:

<form method="post">
  {# Only use async CSRF in this one form: #}
  {{ csrfInput({ async: true }) }}

  {# ... #}
</form>

CSRF over Ajax #

If your front-end needs to send data to Craft over Ajax, it should still use CSRF. See the documentation on Ajax and Forms for framework-agnostic information on retrieving and sending CSRF tokens.

Applies to Craft CMS 5, Craft CMS 4, and Craft CMS 3.