Learn to secure your WordPress REST API against CSRF attacks. Master generating nonces, passing them via headers, and verifying them in your API endpoints.
Previously in this course, we explored Implementing REST API Permission Callbacks for Secure Plugins to ensure only authorized users could access our endpoints. While permission callbacks handle who can access a resource, they don't inherently prevent Cross-Site Request Forgery (CSRF).
CSRF is an attack where a malicious site tricks an authenticated user into performing unwanted actions on your site. Since the browser automatically includes cookies with requests, the REST API might trust the request if it only checks for authentication. This lesson adds a layer of defense: Nonce Verification.
A "nonce" (number used once) is a unique, time-sensitive cryptographic token. In WordPress, it acts as a digital handshake. By requiring a nonce for any state-changing request (POST, PUT, DELETE), we verify that the request originated from our own admin dashboard, not an external, unauthorized source.
We don't want to hardcode tokens. Instead, we generate them server-side and pass them to our JavaScript application. In your main plugin file or wherever you localize your scripts, use wp_create_nonce():
PHP#6A9955">// In your PHP initialization wp_localize_script( 'my-plugin-script', 'wpApiSettings', array( 'root' => esc_url_raw( rest_url() ), 'nonce' => wp_create_nonce( 'wp_rest' ), #6A9955">// The standard WordPress REST nonce ) );
The string 'wp_rest' is the standard action name for the WordPress REST API. By using this, you align your plugin with core security expectations.
When using @wordpress/api-fetch, you don't need to manually append the nonce to every URL or request body. Instead, you can configure the global apiFetch to include the nonce in the X-WP-Nonce header for every request.
In your entry-level JavaScript file (e.g., index.js):
JAVASCRIPTimport apiFetch from CE9178">'@wordpress/api-fetch'; // Set the nonce header globally apiFetch.setFetchHandler( async ( options ) => { const headers = { CE9178">'X-WP-Nonce': window.wpApiSettings.nonce, ...options.headers, }; return await apiFetch( { ...options, headers } ); } );
By setting this handler, you ensure every call made via apiFetch carries the security token required by the server.
Now that the client is sending the header, we must verify it in our endpoint. WordPress handles this automatically if you pass the permission_callback correctly. However, to be explicit or to perform custom checks, you can use wp_verify_nonce():
PHPregister_rest_route( 'kb/v1', '/update', array( 'methods' => 'POST', 'callback' => 'kb_update_post', 'permission_callback' => function( $request ) { #6A9955">// WordPress core automatically checks the X-WP-Nonce header #6A9955">// if you return current_user_can() here. return current_user_can( 'edit_posts' ); }, ) );
If you ever need to verify a nonce manually inside the callback, you would do it like this:
PHPif ( ! wp_verify_nonce( $request->get_header( 'x-wp-nonce' ), 'wp_rest' ) ) { return new WP_Error( 'rest_forbidden', 'Invalid nonce.', array( 'status' => 403 ) ); }
wp_localize_script call in the plugin's PHP file.nonce key to the localized object using wp_create_nonce('wp_rest').apiFetch is configured with the X-WP-Nonce header.X-WP-Nonce.wp_create_nonce('my-custom-action') in PHP but trying to verify with 'wp_rest' in the callback will always fail. Keep your action names consistent.Securing your endpoints with nonces is non-negotiable for intermediate plugin development. We've moved from basic permission checks to robust CSRF protection by generating tokens in PHP, injecting them into the JS environment, and ensuring they are sent via the X-WP-Nonce header. This workflow keeps your Knowledge Base plugin secure against common web vulnerabilities.
Up next: Advanced Sanitization Techniques where we will look at strictly enforcing data schemas for incoming JSON objects.
Learn to enforce strict schemas and validate nested JSON objects in your WordPress REST API to ensure data integrity and security in your plugin.
Read moreProtecting admin screens is vital for plugin security. Learn to enforce user roles, hide menu items, and secure REST API access in your React-based dashboard.
Implementing Nonce Verification
Debugging React in the WordPress Admin
Building Search and Filter Functionality
Internationalization in React
Managing File Uploads via REST API
Optimizing API Response Times
Working with Date and Time in React
Implementing Drag-and-Drop Sorting
Creating Custom Hooks for API Logic
Integrating with Gutenberg Blocks
Handling Conflict Resolution
Building a Modal Confirmation System
Implementing Activity Logging
Using Webpack Aliases
Unit Testing API Endpoints
Unit Testing React Components
Handling Large Datasets with GraphQL
Implementing Real-time Updates with Web