Site icon Code Solution

wp_verify_nonce return false despite correct parameter passed

The question:

I have created a form that have nonce

<form action="<?php echo get_rest_url(null, 'ilms_plugin/new_membership') ?>" method="post" class=' space-y-2 mt-2  p-2 rounded-md '>
        <?php wp_nonce_field( 'new_membership','test_nonce' ); ?>
        <h1 class="pt-2 text-xl">New membership</h1> 
        <div class='pr-2'>Membership name</div><input name="membership_name" type="text" id='shortname' class="w-full"/>
        <div class='pr-2'>Description</div><textarea name="description" id="" cols="30" rows="6" class="w-full"></textarea>
        <div class="pr-2">Availability</div> Day <input name="membership_period_day" type="number" id="membership_period_day" class="w-full" />
        <div class="pr-2">Price</div> $ HKD <input name="membership_price" type="number" id="membership_price" class="w-full" />
        <div><button class='bg-green-500 mt-2 p-2 rounded-md text-white'>Add</button></div>

In my callback in another php file, I have the following:

function new_membership($data){
global $wpdb;
$id= 0;
$nonce = $data['test_nonce'];

if ( !wp_verify_nonce($nonce, 'new_membership') ) {
    var_dump("nonce rotten!");
    exit; // Get out of here, the nonce is rotten!
if (isset ($data['membership_name']) && isset($data['description'])){
    $sql = $wpdb->prepare( "INSERT INTO memberships (membership_name, membership_description, membership_period_day, membership_price) VALUES ( %s, %s, %d, %d)",
        $data['membership_name'], $data['description'], $data['membership_period_day'], $data['membership_price']);
    $id = $wpdb->insert_id;


But the wp_verify_nonce() keep returning false. I dont know what is the reason and how to fix it.

Edit to reply comment:

to Anton: the value of test_nonce is “07213d197c”. On the form I set it like this

<?php wp_nonce_field( 'new_membership','test_nonce' ); ?>

to Sally CJ: yes, I call it using register_rest_route() in my custom_plugin.php

add_action('rest_api_init', function () {

register_rest_route( 'custom_plugin', 'new_membership',[
    'methods'  => 'POST',
    'callback' => 'new_membership',

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

the wp_verify_nonce() keep returning false

If you were logged-in to your (WordPress) site when you used the form, then the above is normal. Here’s why so:

  • Your form submits to a custom REST API endpoint (at /wp-json/ilms_plugin/new_membership) and the default authentication method used by the REST API is cookie-based, i.e. it checks if a nonce with the action named wp_rest is set and that it’s valid (not yet expired), and if not (not set or it’s invalid/expired), then WordPress sets the current user to 0 which affects all nonce-related functions including but not limited to wp_verify_nonce().

  • In simple words, it means functions like wp_verify_nonce() will return false because nonces are based on the current user, so if the user is logged-in to your site (when filling in the form), but not the REST API (when the form submission/data is processed), then wp_verify_nonce() will return false because the user ID becomes 0 in the REST API.

Check out the REST API handbook for more details about the authentication:

How to fix the issue

Just add wp_nonce_field( 'wp_rest', '_wpnonce', false ); somewhere in your form, e.g. right after the opening <form> tag: (Note that I escaped the URL using esc_url())

<form action="<?php echo esc_url( get_rest_url( null, 'ilms_plugin/new_membership' ) ); ?>" ...>
    <?php wp_nonce_field( 'wp_rest', '_wpnonce', false ); ?>

Additional Notes

  1. Instead of get_rest_url( null, 'ilms_plugin/new_membership' ), you could use rest_url( 'ilms_plugin/new_membership' ) which uses rest_url() and is shorter.. 🙂

  2. Instead of using $wpdb->query(), I would use $wpdb->insert() to insert the data to the database.

  3. A REST API endpoint should always set a permission_callback and you should also use the args argument to define the parameters for the endpoint like the test_nonce in your case. Nonetheless, refer to Adding Custom Endpoints in the REST API handbook for further details.

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Exit mobile version