Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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().

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.Support on Ko-Fi

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 0 for 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 $_REQUEST when 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.