WordPress AJAX Returns -1 – Nonce Verification Fix
TL;DR: If your WordPress AJAX request returns -1, it's almost always a
nonce verification failure. This guide walks you through the root cause, the fix, and
best practices to keep your AJAX secure and reliable.
1. Introduction
If you've ever built a custom AJAX handler in WordPress, you've likely encountered the dreaded
-1 response. It's frustrating, cryptic, and often stops your feature dead in its tracks.
The root cause? Nonce verification failure.
In this article, we'll dissect why WordPress returns -1, how nonces work, and — most
importantly — how to fix it once and for all. Whether you're a plugin developer or a theme builder,
this guide will save you hours of debugging.
-1 response is WordPress's way of saying
"I don't trust this request." Fixing it is about earning that trust with proper nonce handling.
2. What is AJAX in WordPress?
WordPress provides a built-in AJAX framework that allows you to send asynchronous requests from the
frontend or admin area to the server. The entry point is admin-ajax.php, which routes
requests to your custom action hooks.
Here's the typical flow:
- JavaScript sends a request to
admin-ajax.phpwith anactionparameter. - WordPress fires
wp_ajax_{action}(for logged-in users) orwp_ajax_nopriv_{action}(for guests). - Your callback function processes the request and returns a response.
When something goes wrong — especially with security — WordPress returns -1 as a fallback.
3. Understanding the -1 Error
The -1 response is WordPress's generic "access denied" or "invalid request" signal. It's
most commonly returned when:
- Nonce verification fails — the
_wpnonceor custom nonce doesn't match. - The action hook is not registered — WordPress can't find your callback.
- Authentication fails — user isn't logged in when required.
- Capability check fails — current user lacks the required permissions.
-1, start by checking
your nonce.
4. What is a Nonce?
A nonce (number used once) in WordPress is a security token that protects your AJAX endpoints from Cross-Site Request Forgery (CSRF) attacks. It's a one-time-use hash that ties a request to a specific user, action, and time window.
WordPress creates nonces using wp_create_nonce() and verifies them with
check_ajax_referer() or wp_verify_nonce().
Create → Send → Verify → Expire (12–24 hours)
5. Common Causes of Nonce Verification Failure
- Missing nonce in request: You forgot to include the nonce in your AJAX data.
- Incorrect nonce action name: The action used in
wp_create_nonce()doesn't match the one incheck_ajax_referer(). - Nonce expired: WordPress nonces typically expire after 12–24 hours.
- User logged out: Nonces are user-specific; if the user logs out, the nonce becomes invalid.
- JavaScript caching: The nonce value is cached and not refreshed after login.
- Wrong hook: Using
wp_ajax_noprivfor logged-in users or vice versa.
6. Step-by-Step Fix
Step 1: Generate the Nonce in PHP
In your WordPress theme's functions.php or in your plugin, create a nonce and pass it to
your JavaScript via wp_localize_script().
// functions.php or plugin file
add_action('wp_enqueue_scripts', 'my_ajax_enqueue');
function my_ajax_enqueue() {
wp_enqueue_script('my-ajax-js', get_template_directory_uri() . '/js/my-ajax.js', ['jquery'], null, true);
wp_localize_script('my-ajax-js', 'my_ajax_obj', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('my_ajax_nonce_action')
]);
}
Step 2: Send the Nonce with Your AJAX Request
In your JavaScript, include the nonce in the data payload.
// my-ajax.js
jQuery(document).ready(function($) {
$('#my-button').on('click', function() {
$.ajax({
url: my_ajax_obj.ajax_url,
type: 'POST',
data: {
action: 'my_ajax_action',
_wpnonce: my_ajax_obj.nonce,
// other data...
},
success: function(response) {
console.log(response);
},
error: function(xhr, status, error) {
console.error(error);
}
});
});
});
Step 3: Verify the Nonce in Your PHP Callback
Inside your AJAX callback, verify the nonce using check_ajax_referer().
// functions.php
add_action('wp_ajax_my_ajax_action', 'my_ajax_callback');
add_action('wp_ajax_nopriv_my_ajax_action', 'my_ajax_callback');
function my_ajax_callback() {
// Verify nonce
check_ajax_referer('my_ajax_nonce_action', '_wpnonce');
// Your logic here...
$response = ['status' => 'success', 'data' => 'Hello from AJAX!'];
wp_send_json($response);
}
Step 4: Debug and Test
If you still get -1, add debugging to see what's happening:
// Debug version
function my_ajax_callback() {
// Check if nonce exists
if (!isset($_POST['_wpnonce'])) {
wp_send_json_error('Nonce missing', 400);
return;
}
if (!wp_verify_nonce($_POST['_wpnonce'], 'my_ajax_nonce_action')) {
wp_send_json_error('Nonce verification failed', 403);
return;
}
// Proceed...
}
wp_send_json_error() with meaningful messages during
development to pinpoint issues faster.
7. Complete Code Example
Below is a fully working example that you can drop into your theme or plugin.
// ==============================
// 1. PHP (functions.php or plugin)
// ==============================
add_action('wp_enqueue_scripts', 'fl365_ajax_enqueue');
function fl365_ajax_enqueue() {
wp_enqueue_script('fl365-ajax', get_template_directory_uri() . '/js/fl365-ajax.js', ['jquery'], '1.0', true);
wp_localize_script('fl365-ajax', 'fl365_ajax', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('fl365_ajax_nonce'),
]);
}
add_action('wp_ajax_fl365_ajax_action', 'fl365_ajax_handler');
add_action('wp_ajax_nopriv_fl365_ajax_action', 'fl365_ajax_handler');
function fl365_ajax_handler() {
// Verify nonce
if (!check_ajax_referer('fl365_ajax_nonce', '_wpnonce', false)) {
wp_send_json_error(['message' => 'Nonce verification failed'], 403);
return;
}
// Your business logic
$data = $_POST['data'] ?? '';
wp_send_json_success(['message' => 'Received: ' . $data]);
}
// ==============================
// 2. JavaScript (fl365-ajax.js)
// ==============================
jQuery(document).ready(function($) {
$('#fl365-btn').on('click', function() {
$.ajax({
url: fl365_ajax.ajax_url,
type: 'POST',
data: {
action: 'fl365_ajax_action',
_wpnonce: fl365_ajax.nonce,
data: 'Hello from frontend!'
},
success: function(res) {
if (res.success) {
alert('✅ ' + res.data.message);
} else {
alert('❌ ' + res.data.message);
}
},
error: function(xhr) {
console.error(xhr.responseText);
}
});
});
});
8. Best Practices
- Always use
check_ajax_referer()— never skip nonce verification. - Use distinct nonce actions for different endpoints.
- Refresh nonces on login — if your app uses long-lived sessions, refresh the nonce periodically.
- Log failed attempts — helps detect attacks and debug issues.
- Use
wp_send_json_success()/wp_send_json_error()for consistent responses. - Test with both logged-in and guest users — ensure both hooks are registered.
- Cache AJAX responses when appropriate to reduce server load.
9. Frequently Asked Questions
Click a question to reveal the answer.
No comments:
Post a Comment
Thanks for your valuable comment...........
Md. Mominul Islam