Elementor Atomic Forms vs. Vinyl/Varnish Cache
The Symptom ¶
At ZFutura, we've recently used Elementor's new Atomic Forms for the first time for new contact forms on our home page. Initially, the forms seemed to work fine, but the next day, our watchdog started to sound the alarm. The watchdog (part of our infrastructure monitoring solution) fills out and submits the forms a few times a day and checks that the submission actually reaches our internal systems.
The submissions were now failing with the error message "Your submission failed because the form is invalid" (or rather its German equivalent "Deine Übermittlung ist fehlgeschlagen, da das Formular ungültig ist."). Manual testing confirmed this. After we edited the page, the forms started working again, which made the problem even more puzzling.
The Cause ¶
When investigating the problem, we noticed that the forms started to fail when the Age header in the HTTP response added by our cache indicated that the pages had been generated about half a day to a day before. Further investigation revealed that Atomic Forms embeds a nonce in the page's source code. This nonce is only valid for 12-24 hours and gets passed to the server when the form is submitted. If the nonce has expired, the form is rejected.
Enter the Cache ¶
A Vinyl cache (formerly known as Varnish) caches the web pages that are generated by WordPress and Elementor for up to 30 days. Changes to the pages usually purge them from the cache, but unfortunately, the nonce change is not communicated to the cache. And this is where the problem lies: The cached page contains a potentially outdated nonce, which will be rejected by the backend form handler.
This explains why the problem was not caught during initial testing and was fixed by updating the page: Initially, the Vinyl cache was fresh and the nonce still valid, but after a certain time the nonce expired and the form began to fail. Updating the page refreshed it in the Vinyl cache, repairing the form temporarily.
The Fix ¶
To work around the problem, we've reduced the lifetime of form pages in the Vinyl cache to one hour, although any lifetime well below 12 hours would work. Elementor even recommends excluding form pages from caching. A more elaborate fix would be to regularly purge and warm the Vinyl cache after the nonce has been renewed. We'll continue to monitor the situation and may implement such a solution if we notice any relevant performance hit.
This problem was previously reported on GitHub as issue elementor/elementor#37331 by user @ciprianpentelescu, but the issue was closed by Elementor, in part because Elementor does not support caches on form pages.