To add AJAX to a WordPress plugin, enqueue a JavaScript file, pass it the correct admin-ajax.php URL and a request nonce, send an action value from JavaScript, and register a PHP handler for that action. In the handler, verify the nonce, check the user’s capability, validate the submitted data, return a response, and end the request. For features available to logged-out visitors, register the separate wp_ajax_nopriv_{$action} hook as well.
How WordPress plugin AJAX requests are routed
WordPress plugins commonly send AJAX requests to wp-admin/admin-ajax.php. The request’s action field tells WordPress which callback to run. A logged-in request is routed through wp_ajax_{$action}; a logged-out request uses wp_ajax_nopriv_{$action}. These are distinct hooks, so register one or both according to the feature’s intended audience.
The WordPress Plugin Handbook’s AJAX guide describes this request flow and demonstrates it with jQuery. Plain JavaScript can also make the request; the appropriate choice depends on the plugin’s existing dependencies and needs.
Enqueue the script and provide its request settings
Enqueue the JavaScript with wp_enqueue_script() rather than embedding it directly in a page. Use the script’s enqueue handle to provide JavaScript with the endpoint URL generated by PHP and a nonce for the intended operation. WordPress’s server-side and enqueuing guide demonstrates passing these values with wp_localize_script().
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match#1 Best Overall
For an admin feature, load the script only on the relevant plugin screen when practical. The handbook shows checking the admin page hook before enqueueing, which avoids loading a plugin’s AJAX code on unrelated screens. Do not hardcode a site-specific admin-ajax.php address into a portable plugin script; generate it with admin_url( 'admin-ajax.php' ).
Register and secure the PHP handler
Use a distinctive action name, such as my_plugin_save_item, and register the matching hook for the audience you intend to serve:
add_action( 'wp_ajax_my_plugin_save_item', 'my_plugin_save_item' );
// Add this only if logged-out visitors should also use the feature:
add_action( 'wp_ajax_nopriv_my_plugin_save_item', 'my_plugin_save_item' );
The callback can then verify the nonce, enforce permission separately, validate the specific data it needs, perform the operation, send the response expected by the client, and terminate the request. A simplified pattern is:
function my_plugin_save_item() {
check_ajax_referer( 'my_plugin_save_item', '_ajax_nonce' );
if ( ! current_user_can( 'edit_posts' ) ) {
wp_send_json_error( array( 'message' => 'You are not allowed to do that.' ), 403 );
}
$item_id = isset( $_POST['item_id'] )
? absint( $_POST['item_id'] )
: 0;
if ( ! $item_id ) {
wp_send_json_error( array( 'message' => 'Invalid item.' ), 400 );
}
// Perform the operation after validating the fields it requires.
wp_send_json_success( array( 'item_id' => $item_id ) );
}
This is a pattern, not a complete plugin: choose a capability that matches the operation, validate every field according to its purpose, and perform the actual operation only after those checks. WordPress’s server-side guide covers the handler flow, while its nonce documentation explains what nonce checks do and do not protect.
Recommended Free Tools
Rank #3
Send the request from JavaScript
Submit the endpoint URL provided by PHP, the same action name used in the hook, the nonce under the field name expected by verification, and only the data the callback needs. For example, using the jQuery approach shown in the Plugin Handbook:
jQuery.post( myPluginAjax.ajaxUrl, {
action: 'my_plugin_save_item',
_ajax_nonce: myPluginAjax.nonce,
item_id: 123
} ).done( function ( response ) {
if ( response.success ) {
// Update the interface using the returned data.
}
} );
The names myPluginAjax, ajaxUrl, and nonce are illustrative: the PHP localization data and JavaScript must use the same object and property names. The action string must also match the suffix used in the registered hook.
Rank #4
Choose whether the action is logged-in-only or public
| Access model | PHP hook | Endpoint considerations | Security considerations |
|---|---|---|---|
| Authenticated users only | wp_ajax_{$action} |
Pass the generated admin-ajax.php URL to the script. |
Check the user’s capability for the requested operation; do not treat a nonce as permission. |
| Logged-in and logged-out visitors | Register both wp_ajax_{$action} and wp_ajax_nopriv_{$action}. |
Pass the URL explicitly. The ajaxurl JavaScript global is not automatically defined for unauthenticated requests. |
Expose or change only what the public action is meant to expose or change; assess abuse and guest-specific CSRF protections. |
Hook behavior is documented for authenticated AJAX actions and unauthenticated AJAX actions. Making a handler public does not make it safe to skip input validation or deliberate access controls.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Important nonce and guest-request limits
- A nonce is not authorization. It helps verify that a request was formed in an expected context, but WordPress explicitly says not to rely on nonces for authentication, authorization, or access control. Use
current_user_can()for permission checks on privileged operations. - A nonce is not necessarily single-use. WordPress’s AJAX guidance notes that a nonce can be reused during its validity window. Nonce validity is tick-based, and session changes can invalidate values; do not use a nonce as a substitute for one-time transaction logic.
- Default guest nonces do not identify individual guests. Logged-out visitors share user ID
0for the default nonce behavior. For sensitive guest operations, consider whether a guest session mechanism and additional CSRF protections are needed. - Read specific request fields. Avoid relying broadly on
$_REQUESTwhen the handler only needs particular values. Validate and sanitize fields for their intended type and use before processing them.
These limits are detailed in WordPress’s nonce guidance and AJAX guide.
Best Value
When server rules interfere with AJAX
If requests to admin-ajax.php fail after a server or hosting security change, check whether access controls on wp-admin are blocking the AJAX endpoint. WordPress’s hardening guidance warns that password-protecting wp-admin can disrupt admin-ajax.php.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

