Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
VGSources
Blog

How to Use AJAX in WordPress: A Secure, Working Guide

A complete WordPress AJAX guide covering admin-ajax.php, action hooks, nonces, capability checks, JSON responses, troubleshooting, and REST API alternatives.
WorldWordPress: A Secure, Working Guide Length10 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To use AJAX in WordPress, load a script through WordPress, pass it the site’s complete admin-ajax.php URL and a nonce, send an action value, then handle that action with matching PHP hooks. Verify the nonce, check the user’s capability, validate the input, and return JSON. For a reusable, resource-oriented interface, register a REST API route instead.

What AJAX does in WordPress

AJAX lets browser code send a request and update part of the current page without a full-page reload. A search suggestion list, cart quantity, “load more” button, inline form, vote control, or admin setting can therefore change while the surrounding page remains in place. WordPress commonly returns JSON, even though the historical name stands for Asynchronous JavaScript and XML.

As an Amazon Associate I earn from qualifying purchases.

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

There are two practical interfaces:

Interface How WordPress dispatches it Best fit Typical authentication Response and reuse
Classic AJAX A POST or GET request to the site-specific wp-admin/admin-ajax.php endpoint containing an action value A small interaction inside a theme or plugin WordPress AJAX nonce, plus a capability check for protected work Usually a compact JSON result tied to one page feature
REST API A request to a registered resource route such as a plugin namespace and version A durable API, resource-oriented data, block editor integration, or several clients wp_rest nonce for cookie-authenticated browser requests; Application Passwords for authenticated external REST calls, available since WordPress 5.6 Structured resources that are easier to reuse outside the original page

Classic AJAX is often the shortest path for one feature. REST is usually clearer when the operation represents a resource, must be consumed by more than one screen, or may later serve a mobile app or another client.

Build a classic WordPress AJAX feature

1. Enqueue the script and pass configuration from PHP

Do not hardcode a relative URL in JavaScript. A site may run in a subdirectory, on a different domain, or in a multisite configuration. Generate the endpoint with admin_url( 'admin-ajax.php' ) and expose it with wp_localize_script(). The same server-side step is a convenient place to create the nonce.

add_action( 'wp_enqueue_scripts', function () {
    wp_enqueue_script(
        'my-plugin-ajax',
        plugin_dir_url( __FILE__ ) . 'assets/ajax.js',
        array( 'jquery' ),
        '1.0.0',
        true
    );

    wp_localize_script(
        'my-plugin-ajax',
        'myAjax',
        array(
            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
            'nonce'   => wp_create_nonce( 'my_plugin_action' ),
        )
    );
} );

The first argument to wp_localize_script() must match the enqueued script handle. WordPress prints an object named myAjax before the JavaScript file, so the browser can read myAjax.ajaxUrl and myAjax.nonce.

For an administration screen, use admin_enqueue_scripts and check the current screen before loading the file. This avoids adding an AJAX script to every dashboard page. WordPress 6.5 and later also support wp_enqueue_script_module() when your implementation is organized as a script module; the endpoint, action, nonce, and server-side checks remain the same.

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

2. Send the request from JavaScript

The request must include an action key. Its value is not a URL and is not automatically derived from the PHP function name: it is the suffix WordPress uses to choose the callback hook.

jQuery.post(
  myAjax.ajaxUrl,
  {
    action: 'my_plugin_action',
    _ajax_nonce: myAjax.nonce,
    value: document.querySelector('#value').value
  }
).done(function (response) {
  if (response.success) {
    document.querySelector('#result').textContent = response.data.message;
  } else {
    document.querySelector('#result').textContent = 'The request was rejected.';
  }
}).fail(function () {
  document.querySelector('#result').textContent = 'The server could not be reached.';
});

jQuery.post() sends form-encoded POST data, which admin-ajax.php accepts. The same protocol works with the browser’s native fetch():

fetch(myAjax.ajaxUrl, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  body: new URLSearchParams({
    action: 'my_plugin_action',
    _ajax_nonce: myAjax.nonce,
    value: document.querySelector('#value').value
  })
})
  .then(function (response) { return response.json(); })
  .then(function (response) {
    if (response.success) {
      document.querySelector('#result').textContent = response.data.message;
    }
  });

Keep the request method, field names, and response handling consistent with the PHP callback. If you use JSON as the request body, decode and validate it explicitly on the server; form encoding is simpler for the classic endpoint.

3. Register the logged-in and logged-out hooks

WordPress uses two hook families. A logged-in request is dispatched through wp_ajax_<action>. A request from a logged-out visitor is dispatched through wp_ajax_nopriv_<action>. The suffix must exactly match the JavaScript action value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
add_action( 'wp_ajax_my_plugin_action', 'my_plugin_action' );
add_action( 'wp_ajax_nopriv_my_plugin_action', 'my_plugin_action' );

function my_plugin_action() {
    check_ajax_referer( 'my_plugin_action' );

    if ( ! current_user_can( 'read' ) ) {
        wp_send_json_error(
            array( 'message' => 'Permission denied.' ),
            403
        );
    }

    $value = isset( $_POST['value'] )
        ? sanitize_text_field( wp_unslash( $_POST['value'] ) )
        : '';

    wp_send_json_success(
        array( 'message' => 'Received: ' . $value )
    );
}

Use the nopriv hook only when anonymous visitors are intentionally allowed to perform the operation. In the example, both hooks are registered but the callback still requires the read capability, so an anonymous request fails the authorization check. For a genuinely public operation, choose a policy that does not rely on a logged-in capability and add rate limiting or other abuse controls appropriate to the feature.

Secure every request correctly

Nonce verification

Create the nonce with wp_create_nonce(), send it as _ajax_nonce (or another field accepted by your verification call), and verify it with check_ajax_referer(). The action string used to create and verify it should describe the operation, such as my_plugin_action. A missing, expired, or action-mismatched nonce causes the verification to fail.

A nonce helps defend against cross-site request forgery; it is not a login credential and does not grant permission. Never treat a valid nonce as proof that a visitor may edit content, change settings, delete records, or access private data.

Capability checks

After nonce verification, authorize the requested operation with current_user_can() or an equivalent capability policy. Use the narrowest capability that fits the action. For example, reading a public value, editing a post, managing options, and deleting a user should not all use the same permission. Return an error status such as 403 when the current user is authenticated but not allowed to continue.

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

Input validation and output

Read only the fields your callback needs. Apply wp_unslash() before sanitizing values from $_POST or $_GET, then validate the resulting type and range. Sanitization is not a substitute for validation: a numeric ID should be checked as an integer, an enumerated setting should be compared with an allow-list, and a URL should be validated for the context in which it will be used.

Use wp_send_json_success() and wp_send_json_error() so the browser receives a predictable object with a Boolean success property and a data member. On the client, handle both branches and the transport failure branch. Do not print PHP notices, template markup, or debugging text into the JSON response.

How WordPress decides whether a request is logged in

The classic endpoint runs in the context of the current WordPress session. A browser carrying a logged-in user’s cookies reaches the wp_ajax_ hook. A visitor without that session reaches the wp_ajax_nopriv_ hook. This is why a callback can appear not to fire even though the action string is correct: only one of the two hooks may have been registered.

  • For a member-only operation, register wp_ajax_<action> and enforce the required capability.
  • For a public operation, register wp_ajax_nopriv_<action> and design validation and abuse protection for unauthenticated traffic.
  • If both audiences are supported, register both hooks and make the callback’s authorization rules explicit rather than assuming the nonce decides access.

Use the REST API when the feature is a resource

A REST route is a better structural fit when the request creates, reads, updates, or deletes a named resource, when several pages need the same endpoint, or when a non-browser client will consume it. Register a namespaced route during rest_api_init and provide a permission_callback; modern WordPress expects that callback to be declared.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
add_action( 'rest_api_init', function () {
    register_rest_route(
        'my-plugin/v1',
        '/value',
        array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => 'my_plugin_rest_value',
            'permission_callback' => function () {
                return current_user_can( 'read' );
            },
            'args'                => array(
                'value' => array(
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_text_field',
                    'validate_callback' => function ( $value ) {
                        return is_string( $value ) && $value !== '';
                    },
                ),
            ),
        )
    );
} );

function my_plugin_rest_value( WP_REST_Request $request ) {
    return rest_ensure_response(
        array(
            'message' => 'Received: ' . $request->get_param( 'value' ),
        )
    );
}

For a cookie-authenticated browser request, create a nonce with wp_create_nonce( 'wp_rest' ) and send it in the X-WP-Nonce header. The _wpnonce request parameter is also supported. An external authenticated client can use Application Passwords, which WordPress has supported since version 5.6; do not expose those credentials in public browser JavaScript.

fetch('/wp-json/my-plugin/v1/value', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-WP-Nonce': myRestNonce
  },
  body: JSON.stringify({ value: 'Example' })
})
  .then(function (response) { return response.json(); })
  .then(function (data) {
    document.querySelector('#result').textContent = data.message;
  });

When the route is public, use an intentional public permission_callback and apply validation, pagination, caching, and rate controls appropriate to the data. When it is private, let the permission callback express the policy and return a WP_Error or a false result for unauthorized users.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Designing a dependable JSON response

Choose a stable response contract before writing the interface. A successful classic AJAX response from wp_send_json_success() has the shape { success: true, data: ... }; an error response has success: false. Put machine-readable fields inside data, and keep human-facing messages separate from values the client must parse.

  • Return an appropriate HTTP status for authorization and validation failures.
  • Use one predictable field name for each value; do not make the client infer meaning from a formatted sentence.
  • Handle malformed or empty input on the server even if the browser validates it first.
  • Make the UI disable duplicate submissions or show a pending state when the operation is not idempotent.
  • Log details privately during development, but never mix notices or stack traces into the production response.

Troubleshoot a request that does not work

The callback never runs

  1. Open the browser’s Network panel and confirm that the request URL is the localized admin-ajax.php value, not a guessed relative path.
  2. Inspect the submitted fields and verify that action is present and spelled exactly like the PHP hook suffix.
  3. Check the browser’s login state. A logged-in request needs wp_ajax_<action>; a logged-out request needs wp_ajax_nopriv_<action>.
  4. Confirm the script was actually enqueued on the page and that the localized object exists before the file executes.

The response says the nonce is invalid

Regenerate the nonce through the enqueue and localization path, then compare the nonce action used by wp_create_nonce() with the action expected by check_ajax_referer(). A cached page can serve stale localized data, and a typo in the submitted field name can leave the callback without the nonce it expects.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

The request returns an HTML error or an empty body

Look for PHP warnings, fatal errors, or an early die before the JSON function runs. Ensure every execution path ends with a JSON success or error response. In development, inspect the server log rather than echoing diagnostics into the response consumed by JavaScript.

The request works for administrators but not other users

Compare the capability check with the role that is testing the feature. Administrators may pass a broad capability while ordinary users do not. Replace an overly broad permission with the exact capability the operation requires, or revise the feature’s access policy deliberately.

An admin feature loads on the wrong screens

Move the enqueue logic to admin_enqueue_scripts, inspect the supplied screen hook or screen object, and enqueue the script only for the relevant page. This reduces unnecessary requests and prevents unrelated admin pages from receiving feature-specific JavaScript.

Classic AJAX or REST: a practical decision

  • Choose classic AJAX for one theme or plugin interaction whose server callback is naturally an action, especially when you need a quick form submission or button response.
  • Choose REST when the endpoint represents posts, products, settings, records, or another resource; when multiple screens need it; or when the response should remain useful to other clients.
  • Keep security independent of the interface. Both approaches require validation and authorization. A REST nonce and a classic AJAX nonce solve request-integrity concerns, not capability decisions.
  • Plan for reuse. A narrowly scoped classic callback can later be replaced by a REST route, but a stable resource model and response schema are easier to extend from the beginning.

Testing and deployment checklist

  1. Test the feature while logged in as an allowed user, a logged-in user without the required capability, and a logged-out visitor when that audience matters.
  2. Use the Network panel to verify the URL, method, action, nonce field or header, status code, and response JSON.
  3. Try missing, empty, malformed, oversized, and unauthorized input; the server must reject it safely.
  4. Confirm the interface handles success, a structured application error, a 403 response, and a network failure.
  5. Test on sites installed in a subdirectory and, where relevant, on multisite so hardcoded paths do not slip through.
  6. For admin functionality, verify that the script is absent from unrelated dashboard screens.
  7. Remove debugging output and confirm that production responses contain only the documented JSON contract.

The Bottom Line

Use admin-ajax.php with a localized URL, matching action hooks, a verified nonce, capability checks, and structured JSON for small page interactions. Use a REST route when the feature is a reusable resource API. In either case, authorization and server-side validation are mandatory; a nonce alone is never permission.

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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.