
The WordPress REST API Requests opens doors for building dynamic, decoupled applications, but security remains paramount. Nonces (Number Used Once) are critical for verifying the authenticity of requests, preventing CSRF (Cross-Site Request Forgery) attacks, and ensuring only authorized users can perform actions. This guide explains how to securely integrate a JavaScript front end with WordPress nonce system and REST API, covering nonce generation, request handling, and error management.
Why Secure WordPress REST API Requests With Nonces Matter
Nonces protect against:
- CSRF Attacks: Malicious sites tricking users into submitting unintended requests.
- Unauthorized Access: Blocking requests from users without proper permissions.
- Replay Attacks: Preventing intercepted requests from being reused.
Example Scenario:
A front-end form allows users to delete posts. Without a nonce, an attacker could forge a request to delete arbitrary posts.
Step 1: Generating and Passing Nonces to the Front End
1.1 Create a Nonce in PHP
Use wp_create_nonce() to generate a nonce for specific actions (e.g., wp_rest for REST API requests).
// PHP Code
// functions.php or plugin file Â
function enqueue_scripts() {Â Â
  wp_enqueue_script('my-app', get_template_directory_uri() . '/js/app.js', array(), '1.0', true); Â
  // Generate a nonce for REST API requests Â
  $nonce = wp_create_nonce('wp_rest'); Â
  // Pass data to JavaScript Â
  wp_localize_script('my-app', 'wpApiSettings', array( Â
    'root' => esc_url_raw(rest_url()), Â
    'nonce' => $nonce, Â
    'user' => get_current_user_id() Â
  )); Â
}Â Â
add_action('wp_enqueue_scripts', 'enqueue_scripts');
Key Functions:
- wp_create_nonce(‘wp_rest’): Generates a nonce for REST API authentication.
- wp_localize_script(): Safely passes PHP data to JavaScript.
1.2 Alternative: Generate Nonces via REST
For logged-out users, expose a REST endpoint to fetch nonces:
// PHP Code
add_action('rest_api_init', function () {Â Â
  register_rest_route('myplugin/v1', '/nonce', array( Â
    'methods' => 'GET', Â
    'callback' => function () { Â
      return new WP_REST_Response(array( Â
        'nonce' => wp_create_nonce('wp_rest') Â
      ), 200); Â
    }, Â
    'permission_callback' => '__return_true' Â
  )); Â
});
Step 2: Including Nonces in Front-End Requests
2.1 Using Fetch API
Attach the nonce to the X-WP-Nonce header in REST requests:
// Javascrpt
// app.js
fetch(wpApiSettings.root + 'myplugin/v1/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce
},
body: JSON.stringify({ title: 'New Post' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
2.2 Using Axios
Set the nonce as a default header:
// app.js
axios.defaults.headers.common['X-WP-Nonce'] = wpApiSettings.nonce;
axios.post(wpApiSettings.root + 'myplugin/v1/posts', {
title: 'New Post'
})
.then(response => console.log(response.data))
.catch(error => console.error('Error:', error));
2.3 For AJAX (jQuery)
Include the nonce in the beforeSend hook:
// AJAX Code
// app.js
$.ajax({
url: wpApiSettings.root + 'myplugin/v1/posts',
method: 'POST',
data: { title: 'New Post' },
beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce', wpApiSettings.nonce);
},
success: function(data) {
console.log(data);
},
error: function(error) {
console.error('Error:', error);
}
});
Step 3: Validating Nonces in REST Endpoints
Verify the nonce in your REST API callback functions.
// PHP Code
add_action('rest_api_init', function () {
register_rest_route('myplugin/v1', '/posts', array(
'methods' => 'POST',
'callback' => function (WP_REST_Request $request) {
// Verify nonce
$nonce = $request->get_header('X_WP_Nonce');
if (!wp_verify_nonce($nonce, 'wp_rest')) {
return new WP_Error('invalid_nonce', 'Invalid nonce.', array('status' => 403));
}
// Proceed with authenticated logic
$post_id = wp_insert_post(array(
'post_title' => $request->get_param('title')
));
return new WP_REST_Response(array('id' => $post_id), 200);
},
'permission_callback' => function () {
return current_user_can('publish_posts');
}
));
});
Note: WordPress automatically checks wp_rest nonces for REST API routes. For custom actions, use wp_verify_nonce($nonce, ‘action_name’).
Step 4: Handling Nonce Expiration
Nonces expire after 24 hours by default. Handle expiration gracefully by:
4.1 Detecting Expired Nonces
Check for a rest_cookie_invalid_nonce error in responses:
// javascript
// app.js
fetch(wpApiSettings.root + 'myplugin/v1/posts', {
headers: { 'X-WP-Nonce': wpApiSettings.nonce }
})
.then(response => {
if (response.status === 403) {
// Nonce expired – refresh it
return refreshNonce();
}
return response.json();
})
.then(data => console.log(data));
// Fetch a new nonce
function refreshNonce() {
return fetch(wpApiSettings.root + 'myplugin/v1/nonce')
.then(response => response.json())
.then(data => {
wpApiSettings.nonce = data.nonce;
localStorage.setItem('nonce', data.nonce); // Optional: Cache
});
}
4.2 Automatically Refreshing Nonces
Use an interceptor (Axios example):
// Javascript
// app.js
axios.interceptors.response.use(
response => response,
error => {
if (error.response.status === 403) {
return refreshNonce().then(() => {
return axios.request(error.config);
});
}
return Promise.reject(error);
}
);
Step 5: Security Best Practices
- Nonce Lifetime: Reduce expiration time for sensitive actions using the nonce_life filter:
// PHP Code
add_filter('nonce_life', function () {Â Â
  return 12 * 3600; // 12 hours Â
});
- Capability Checks: Always pair nonces with permission_callback in REST endpoints.
- Avoid Logging Nonces: Ensure nonces don’t appear in server logs or error messages.
- HTTPS: Enforce HTTPS to encrypt nonces during transmission.
- Rate Limiting: Prevent brute-force attacks by limiting requests to sensitive endpoints.
Real-World Use Cases
- Front-End Forms: Secure contact forms, user registration, or content submission.
- Real-Time Updates: Live notifications or chat systems using REST and WebSockets.
- User Authentication: Integrate with OAuth/OpenID while maintaining WordPress sessions.
Frequently Asked Questions
Conclusion
Integrating WordPress nonces with a JavaScript front end ensures secure communication with the REST API, protecting against CSRF and unauthorized access. By generating nonces server-side, attaching them to requests, and handling expiration gracefully, you can build robust, secure applications that leverage WordPress backend capabilities. Always pair nonces with capability checks, HTTPS, and other security measures to create a defense-in-depth strategy.
With this guide, you’re equipped to implement secure, nonce-authenticated interactions in your next WordPress project.
About the writer
Hassan Tahir wrote this article, drawing on his experience to clarify WordPress concepts and enhance developer understanding. Through his work, he aims to help both beginners and professionals refine their skills and tackle WordPress projects with greater confidence.