Summary
I want to include a form in a WordPress page that includes a nonce field, and upon submission it validates the nonce, and for this to work fine when full page caching used.
Issue with full page caching
This works until a website uses a full page caching mechanism, as the page code is cached including the nonce field.
If the cache is not configured to regenerate every 10 hours or less then when submitting the form the nonce is going to be invalid. The 10 hours or less is based on this information, which is for the WP Rocket plugin, but appears to be the case for most caching mechanisms.
Therefore if you want to include a form in a WordPress page, and validate the nonce, on a website which has full page caching enabled, what is the solution? Is the only way to configure the cache to regenerate every 10 hours or less?
Note: that I am wanting to do the nonce check, in part, because when running WordPress Coding Standards phpcs (code sniffer) it will highlight an error that the form fields being submitted must be validated with a nonce.
Example:
Here is a very basic example form that I've included at the top of WordPress pages, a nonce field is generated and included in the form via wp_nonce_field
, on submission it will display an "Nonce invalid" notice if the nonce could not be verified.
On websites without full page caching the nonce is valid
On websites with full page caching the nonce is invalid if the page being served from the cache is > 10 hours old
function test_form() {
?>
<form method="post">
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" value="Doe"><br><br>
<?php wp_nonce_field( 'test_form', 'test_form_nonce' ); ?>
<input type="submit" value="Submit">
</form>
<?php
if ( !wp_verify_nonce( sanitize_key( $_POST['test_form_nonce'] ), 'test_form' ) ) {
echo 'Nonce invalid';
}
}
add_action( 'wp_head', 'test_form' );
Summary
I want to include a form in a WordPress page that includes a nonce field, and upon submission it validates the nonce, and for this to work fine when full page caching used.
Issue with full page caching
This works until a website uses a full page caching mechanism, as the page code is cached including the nonce field.
If the cache is not configured to regenerate every 10 hours or less then when submitting the form the nonce is going to be invalid. The 10 hours or less is based on this information, which is for the WP Rocket plugin, but appears to be the case for most caching mechanisms.
Therefore if you want to include a form in a WordPress page, and validate the nonce, on a website which has full page caching enabled, what is the solution? Is the only way to configure the cache to regenerate every 10 hours or less?
Note: that I am wanting to do the nonce check, in part, because when running WordPress Coding Standards phpcs (code sniffer) it will highlight an error that the form fields being submitted must be validated with a nonce.
Example:
Here is a very basic example form that I've included at the top of WordPress pages, a nonce field is generated and included in the form via wp_nonce_field
, on submission it will display an "Nonce invalid" notice if the nonce could not be verified.
On websites without full page caching the nonce is valid
On websites with full page caching the nonce is invalid if the page being served from the cache is > 10 hours old
function test_form() {
?>
<form method="post">
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" value="Doe"><br><br>
<?php wp_nonce_field( 'test_form', 'test_form_nonce' ); ?>
<input type="submit" value="Submit">
</form>
<?php
if ( !wp_verify_nonce( sanitize_key( $_POST['test_form_nonce'] ), 'test_form' ) ) {
echo 'Nonce invalid';
}
}
add_action( 'wp_head', 'test_form' );
If something on the page expires sooner than that pages cache then you will end up in this situation or an analogue/equivalent situation.
So in the strictest sense the answer is a hard no.
Either the nonce expiration has to increase, making the nonce less secure, the cache has to decrease so that the things being cached don't become stale, or that page/page fragment can't be cached. Everything else involves fundamentally changing what you're doing, e.g. pulling the form in via JS, using an iframe or embed of some sort, not using a nonce, etc
Note as well that WP doesn't just check if the nonce is currently valid, there's a grace period where it also checks the value the nonce would have had previously so that stale nonces don't just instantly expire.
Depending on your use case, the use of nonces here might not be correct, e.g. if it were a form int he admin area or for a privileged/logged in user to perform an action then a nonce is appropriate. But for lets say a contact form that has no authentication a nonce may not be appropriate. Gravity Forms for example does not uses nonces unless there are restrictions around the form or it requires user login.
As with most rules, PHPCS is there to catch issues and be a tool to better your code, but you shouldn't blindly implement its recommendations without considering the context. You can silence it with a comment, just be sure to add your own comment explaining why you aren't using nonces and what you've done instead to protect the form ( e.g. captchas ).
As for AJAX, it defeats the point of the nonce since now anybody can query to get the nonce from AJAX without opening the page, eliminating the point of having the nonce in the first place to ensure that the submission of the form came from that specific place. You also then run into the problem of cached AJAX entries, and that you might need a nonce to securely retrieve it putting you back at square one.
The general recommendation is to use JavaScript with AJAX to dynamically populate the nonce (untested):
add_action( 'wp_ajax_nopriv_nonce', static function () : string {
echo wp_create_nonce( 'test_form' );
} );
Reference https://stackoverflow.com/questions/43557755/how-to-call-ajax-in-wordpress on making the AJAX call.
let nonce = value_from_ajax_call;
document.querySelector( '#test_form_nonce' ).attr( 'value', nonce );
The problem stems from trying to use nonce where there is no need in it. Nonce in general should protect form submittion (or any kind of mutating action done a privelaged user). It should be rare that a cached page is sent to a logged in user, therefor it will be generated dynamically and you can safely add the nonce without issues of cache expiry. On the server side you check the nonce only if the user is logged in.