cms
cms copied to clipboard
CSRF token mismatch full cache strategy
Bug description
If I don't use the full cache strategy on my site, the forms work fine. The problem comes when they are used. If I try to send the form, I get the following: CSRF token mismatch.
It seems that the token is not being updated behind the scenes.
I tried wrapping the form with a {{ nocache }} and it was fixed.
Then I realized that if I send the form in Spanish without filling in the inputs, I get the error messages in Spanish. So far so good. If I go to the English site and send it, it shows me the messages in English. Also good. But if I go back to Spanish, it shows me the English messages.
According to the documentation, if full cache is used, we must use {{ nocache }} https://statamic.dev/static-caching#csrf-tokens
Same here: https://statamic.dev/forms#caching
How to reproduce
Enabling the full cache strategy and multisite.
Logs
No response
Environment
Laravel Version: 11.16.0
PHP Version: 8.2.20
Composer Version: 2.7.9
Environment: production
Debug Mode: OFF
Maintenance Mode: OFF
Timezone: UTC
Locale: es
Cache
Config: CACHED
Events: NOT CACHED
Routes: CACHED
Views: CACHED
Drivers
Broadcasting: log
Cache: file
Database: sqlite
Logs: stack / single
Mail: smtp
Queue: sync
Session: file
Statamic
Addons: 5
Sites: 2
Stache Watcher: Disabled (auto)
Static Caching: full
Version: 5.16.0 PRO
Statamic Addons
spatie/statamic-responsive-images: 5.0.0
studio1902/statamic-peak-browser-appearance: 3.5.0
studio1902/statamic-peak-commands: 8.4.0
studio1902/statamic-peak-seo: 8.15.3
studio1902/statamic-peak-tools: 6.3.0
Installation
Starter Kit using via CLI
Additional details
No response
The problem also occurs when doing the opposite with the application languages. If I access English first and then Spanish and go back to English, it returns the messages in Spanish.
It depends on the first page I cache. I've been debugging and it comes from this function in src/Http/Requests/FrontendFormRequest.php:
public function validateResolved()
{
// If this was submitted from a front-end form, we want to use the appropriate language
// for the translation messages. If there's no previous url, it was likely submitted
// directly in a headless format. In that case, we'll just use the default lang.
$site = ($previousUrl = session()->previousUrl()) ? Site::findByUrl($previousUrl) : null;
return $this->withLocale($site?->lang(), fn () => parent::validateResolved());
}
You shouldn't need to wrap the form in the {{ nocache }} tag. CSRF tokens should be automatically replaced when using full-measure caching.
Are you using the native {{ form }} tag? Are there any console errors when viewing a cached page?
You shouldn't need to wrap the form in the
{{ nocache }}tag. CSRF tokens should be automatically replaced when using full-measure caching.Are you using the native
{{ form }}tag? Are there any console errors when viewing a cached page?
Now I was testing locally and I am not getting the CSRF mismatch error. I was watching production. I'll see if the token expires and try again to show you the error.
But it keeps happening that when a page is cached, whether it is English or Spanish, and I submit a form, I get error messages for the wrong language.
But it keeps happening that when a page is cached, whether it is English or Spanish, and I submit a form, I get error messages for the wrong language.
That's with the {{ nocache }} removed, yeah?
How are you submitting the forms? With an AJAX request or just a normal <form> submit?
But it keeps happening that when a page is cached, whether it is English or Spanish, and I submit a form, I get error messages for the wrong language.
That's with the
{{ nocache }}removed, yeah?
Yes, exactly.
If I access the English form without having any page cached, I get the error messages in English. If I go to Spanish and submit the form, I get the error messages in Spanish. Now once both are cached, if I go back to the English form, I get the Spanish messages. The same thing happens in a different order.
Look now:
I have deleted the browser cookies. Now both pages are cached. If I try to submit the form again I get what I told you above.
This happens both with and without the {{ nocache }} tag.
How are you submitting the form? Are you using AJAX?
I'm using precognition. It's the peak studio component but I've extracted the form submission to a dedicated js file.
This is my sending.js:
export default () => ({
success: false,
submitted: false,
form: null,
init() {
this.form = this.$form(
'post',
this.$refs.form.getAttribute('action'),
JSON.parse(this.$refs.form.getAttribute('x-data')).form,
{
headers: {
'X-CSRF-Token': {
toString: () => this.$refs.form.querySelector('[name="_token"]').value,
}
}
}
)
},
submit() {
this.submitted = true
this.form.submit()
.then(response => {
this.form.reset()
this.$refs.form.reset()
this.success = true
this.submitted = false
setTimeout(() => {
this.success = false
}, 4500)
})
.then(this.$refs.form.scrollIntoView())
.catch(error => {
const summary = document.querySelector('#summary')
if (summary) {
this.$focus.focus(summary.querySelector('a'))
}
else {
console.log(error)
}
})
}
});
When you visit one of your cached form pages, do you see an AJAX request to /!/nocache being done automatically?
When you visit one of your cached form pages, do you see an AJAX request to
/!/nocachebeing done automatically?
If I load a page cached, I can see: /!/nocache with status 200
When send the form to: /!/forms/contact 419 unknown status
@duncanmcclean I create a new project if you need test error: https://github.com/danielreales7/test-cache-form
I did a clean installation of Statamic 5 (with Peak Studio) without any configuration. If you try to send the form with all the fields empty, it shows the error messages correctly.
If you now add STATAMIC_STATIC_CACHING_STRATEGY=full to the .env, you cache the page, once cached, try to delete cookies from the browser and try to send the form.
You will see in the console:
https://github.com/user-attachments/assets/de76bd78-9d85-4a7a-a57a-d01173209b30
I am attaching a video of something I just realized. If it is sent without being cached, it shows the errors. When it is cached and I delete cookies, it does not show the errors. However, if I write on any input and send the form again, it shows the errors again.
It seems when a page is cached and you submit an empty form you get a token mismatch error. As soon as you start filling in fields stuff kicks back in.
Is it possible you're submitting before the nocache JS has run and replaced the csrf token in the form?
No. Nocache is loaded before submitting the form. If I don't use cache it works fine.
This issue has not had recent activity and has been marked as stale — by me, a robot. Simply reply to keep it open and send me away. If you do nothing, I will close it in a week. I have no feelings, so whatever you do is fine by me.
Go away, bot.