What Is a Nonce in WordPress, and Why Do Forms Need One?

How Nonces Protect Against CSRF

Without this protection, a malicious website could craft a hidden form or request that, if a logged-in WordPress user's browser happened to visit it, would silently submit an action to the WordPress site using that user's existing authenticated session — deleting content, changing settings, anything the form's target action could do. A nonce, generated specifically for that user's session and that specific action, defeats this because the malicious site has no way to know or predict the correct nonce value to include.

Why This Sometimes Causes Visible Errors

  • Nonces expire after a limited time (commonly around 24 hours, though effectively shorter in practice for a form left open), so a form submitted long after the page loaded can fail verification, producing a "security check failed" style error — covered in more detail for the WooCommerce checkout context elsewhere.
  • A cached page serving a stale, already-expired nonce is a common cause of this error appearing more often than expected, since the embedded nonce was generated at the time the page was originally rendered and cached, not when the visitor actually loaded it.
  • Custom AJAX functionality built without proper nonce verification is a real, common security gap in custom development, worth checking for in any custom form or AJAX handler.

Working With Nonces Correctly

  1. Always include and verify a nonce in any custom form or AJAX handler that performs a meaningful action, using WordPress's built-in wp_nonce_field() and check_admin_referer()/wp_verify_nonce() functions.
  2. Exclude forms and AJAX-heavy pages from page caching, since a cached, stale nonce is a common cause of confusing verification failures.
  3. If seeing "security check failed" errors frequently, check caching configuration first, as covered elsewhere in more detail.

Using nonces correctly in custom code

For a form, add the nonce field when rendering it and verify it when processing it:

// When rendering the form:
wp_nonce_field( 'save_my_form', 'my_form_nonce' );

// When handling the submission:
if ( ! isset( $_POST['my_form_nonce'] )
	|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['my_form_nonce'] ) ), 'save_my_form' ) ) {
	wp_die( 'Security check failed.' );
}

For AJAX handlers, check_ajax_referer() does the same job in one call.

A nonce is not a permission check

A nonce shows that a request came from a page your site generated for that user. It doesn't show that the user is allowed to do the action, so always check capability as well, for example with current_user_can(). Nonces also have a limited lifetime, roughly twelve to twenty-four hours, which is why a page left open overnight can fail. If a page containing a nonce is cached, fetch the nonce at load time instead, or exclude the page from caching.

Need custom form/AJAX functionality built with proper security? See WordPress bug fix.

Available for new projects

NEXT STEP

Rather have it fixed than keep reading?

Based in Dhaka, Bangladesh — serving clients worldwide. Tell me what's happening and you'll have a reply within one working day.

  • Reply within one working day
  • Fixed quote before work starts
  • UK, EU and US hours covered