Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk10 min

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

A complete WordPress AJAX guide covering enqueueing scripts, passing the endpoint and nonce, JavaScript requests, PHP hooks, security, troubleshooting and when to choose the REST API.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use AJAX in WordPress when a page must send or receive data without a full browser reload. The usual pattern is: enqueue a JavaScript file, pass it the site’s generated admin-ajax.php URL and a nonce, send an action value, handle the JSON response, and register matching PHP hooks. Protect the callback with nonce verification, input validation and a capability check; use the REST API instead when you are designing a reusable, resource-oriented endpoint.

What AJAX does in WordPress

AJAX is a browser request that retrieves information or submits data asynchronously, then updates part of the current page. The visitor can search, filter, save, vote or submit a form while the rest of the document remains in place. WordPress commonly returns JSON, so “AJAX” does not require an XML response.

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.

WordPress has two practical interfaces for this work:

  • Classic AJAX: requests go to the site-specific wp-admin/admin-ajax.php endpoint and identify a PHP callback with an action value.
  • REST API: requests go to a registered route such as /wp-json/... and model a resource or operation directly.

For a small interaction inside a theme or plugin, classic AJAX is often the fastest implementation. For an API that multiple pages, applications or integrations will reuse, a REST route normally gives you a clearer contract.

The classic WordPress AJAX request lifecycle

A working request connects five pieces. If one name or hook does not match, the callback will not run.

  1. PHP enqueues the JavaScript through WordPress and supplies the generated AJAX URL.
  2. PHP creates a nonce for the operation and exposes it to the script.
  3. JavaScript sends the URL, an action, the nonce and any form values.
  4. WordPress dispatches the request to wp_ajax_<action> for logged-in users or wp_ajax_nopriv_<action> for logged-out users.
  5. The callback verifies the nonce, checks authorization, validates input and returns a predictable JSON success or error response.

The endpoint is not necessarily the same on every installation. Subdirectory sites, multisite setups and unusual configurations make hardcoded paths fragile, so generate it with admin_url( 'admin-ajax.php' ) and pass it to the browser.

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

Step 1: Enqueue the script and pass configuration

Load the file with WordPress rather than adding a raw <script> tag. On a front end, use wp_enqueue_scripts. On an administration screen, use admin_enqueue_scripts and restrict loading to the relevant screen.

<?php
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' ),
        )
    );
} );

wp_localize_script() prints a JavaScript object before the enqueued file. In this example the browser receives myAjax.ajaxUrl and myAjax.nonce. The object name must be a valid, unique JavaScript identifier. Although the function was named for translated strings, it is also the established way to pass small PHP-generated configuration values to a script.

Do not expose secrets in this object. A nonce is intentionally visible to the browser; it helps prove that a request came from a page generated for your site, but it is not a password.

Since WordPress 6.5, script modules can be loaded with wp_enqueue_script_module(). Modules do not change the AJAX protocol: pass the generated endpoint and nonce to the module, then send the same action and security fields.

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

Step 2: Send the request from JavaScript

Using jQuery

If the script depends on WordPress’s bundled jQuery, jQuery.post() is a concise option.

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.';
});

The string my_plugin_action must exactly match the suffix in the PHP hooks. The conventional nonce field is _ajax_nonce; that is what check_ajax_referer() checks by default. Handle both the JSON-level success: false result and a transport failure such as a network error or an invalid server response.

Using the Fetch API

Modern JavaScript can submit the same fields with fetch(). Because PHP examples usually read $_POST, encode the body as URL-encoded form data.

const form = new URLSearchParams({
  action: 'my_plugin_action',
  _ajax_nonce: myAjax.nonce,
  value: document.querySelector('#value').value
});

fetch(myAjax.ajaxUrl, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
  },
  body: form.toString()
})
  .then((response) => response.json())
  .then((response) => {
    const result = document.querySelector('#result');
    result.textContent = response.success
      ? response.data.message
      : 'The request was rejected.';
  })
  .catch(() => {
    document.querySelector('#result').textContent = 'The server could not be reached.';
  });

The method can be GET for a read-only operation, but use the method your callback and caching policy expect. For changes to site data, POST is the safer normal choice.

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

Step 3: Register and secure the PHP callback

Register the logged-in hook for authenticated users. Add the logged-out hook only when the feature is intentionally available to visitors who are not signed in.

<?php
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 )
    );
}

wp_create_nonce( 'my_plugin_action' ) and check_ajax_referer( 'my_plugin_action' ) must use the same action name. The verification call rejects a missing, stale or mismatched nonce and stops the callback unless you explicitly choose a different failure behavior.

The capability test is separate and essential. Nonces are not authentication and do not authorize an operation. For an administrator-only change, check the capability that actually protects that setting, such as an appropriate management capability, rather than relying on a generic check. For a logged-in user feature, check the capability or ownership rule that applies to that user.

The sample registers wp_ajax_nopriv_ but then requires the read capability. That combination will deny normal logged-out visitors because they have no WordPress user capability. Keep the capability check for a signed-in operation and omit the public hook, or design a genuinely public callback with appropriate rate limits, validation and abuse controls. Never remove authorization merely to make a request work.

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

Validate, sanitize and return a stable response

Sanitizing input is not the same as deciding whether the input is allowed. Apply the function that matches the field, then validate its meaning and limits before using it.

  • Use wp_unslash() before processing values read from request arrays.
  • Use sanitize_text_field() for a simple text value, but do not use it as a substitute for validating an email, integer, URL, identifier or selected option.
  • Check required fields, allowed values, numeric ranges and object ownership in PHP; browser-side checks can be bypassed.
  • Escape data for its output context. In the example, assigning to textContent avoids interpreting returned text as HTML.
  • Return wp_send_json_success() or wp_send_json_error() instead of printing arbitrary markup or PHP warnings.

Successful responses conventionally contain {"success":true,"data":...}; errors contain success: false and can include a message or structured error data. A suitable HTTP status, such as 403 for a permission failure, helps clients distinguish authorization errors from a successful request whose business result happens to be negative.

How logged-in and logged-out requests differ

Visitor state Required PHP hook Typical use Security decision
Signed in wp_ajax_<action> User-specific data, account actions or administration Check the capability and, where relevant, ownership of the record
Signed out wp_ajax_nopriv_<action> Deliberately public searches, previews or submissions Do not assume a nonce makes it safe; validate aggressively and consider throttling and spam controls

WordPress chooses the hook based on the visitor’s authentication state. If a public visitor sends the correct action but only wp_ajax_<action> is registered, no callback will handle the request. Conversely, adding the nopriv hook exposes an operation to the public, so make that an intentional product and security decision.

Classic AJAX or the REST API?

Both approaches can return JSON and both can be secured. Choose according to the shape and expected lifetime of the interface rather than by habit.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Decision point admin-ajax.php REST API route
Endpoint model One site-specific endpoint; an action selects a PHP hook A named route represents a resource or operation
Best fit A focused interaction in one theme or plugin screen A durable interface reused by several pages, applications or integrations
Authentication and nonce Usually a WordPress AJAX nonce in _ajax_nonce, verified with check_ajax_referer() For cookie-authenticated requests, create a wp_rest nonce and send it in X-WP-Nonce or _wpnonce
Dispatch wp_ajax_<action> and optionally wp_ajax_nopriv_<action> Registered route, HTTP method, permission callback and route arguments
Reuse outside the current page Possible, but callers must know the action-based contract Designed for clients that consume a resource-oriented contract
Public versus user-specific work Either is possible; you must register the appropriate hook and write the authorization logic Either is possible; define a route permission callback and authentication method

Use a REST route when the feature is naturally “list, read, create, update or delete this resource,” or when another client will consume it later. Use classic AJAX when a small page interaction already fits a single callback and there is no benefit in designing a broader API.

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

Calling a WordPress REST route securely

A REST implementation begins by registering a route with a permission callback and a callback that returns data. The exact route, schema and capability depend on the resource, so do not treat a REST URL as a replacement string for admin-ajax.php.

For a browser request authenticated by WordPress cookies, create a nonce with the wp_rest action and send it in the X-WP-Nonce header. The nonce can also be supplied as the _wpnonce parameter. The route’s permission callback must still enforce the required capability; the nonce does not grant access.

fetch('/wp-json/my-plugin/v1/items', {
  headers: {
    'X-WP-Nonce': myRest.nonce
  }
})
  .then((response) => response.json())
  .then((items) => {
    // Render the resource returned by the route.
  });

For authenticated external REST calls, WordPress Application Passwords are available beginning with WordPress 5.6. They are intended for API authentication, not for exposing a browser page’s secret. Choose the authentication method that matches the client and never embed a long-lived credential in front-end JavaScript.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Admin-screen and performance considerations

On an administration screen, attach the enqueue callback to admin_enqueue_scripts. Use the supplied screen identifier to load the AJAX file only where it is needed. Loading the script site-wide increases page weight and expands the number of places where an interface can be triggered without providing a benefit.

AJAX removes a full-page navigation; it does not make server work free. Keep database queries bounded, return only the fields the interface needs, and prevent duplicate submissions while a request is pending. For expensive jobs, an asynchronous request may need to start work and poll a status resource rather than hold the browser request open.

Troubleshooting: find the failing layer

The request never reaches the callback

  1. Open the browser’s Network panel and confirm the request URL is the localized admin-ajax.php value, not a guessed path.
  2. Confirm the request method and inspect the submitted form data.
  3. Verify that action is present and is spelled exactly like the PHP hook suffix.
  4. Determine whether the browser is logged in. A logged-in request needs wp_ajax_<action>; a logged-out request needs wp_ajax_nopriv_<action>.

The response says the nonce is invalid

  • Check that PHP created the nonce with the same action string passed to check_ajax_referer().
  • Check that JavaScript sends the expected field, normally _ajax_nonce.
  • Regenerate the value through the enqueue and localization path if the page was cached with an old nonce.
  • Make sure the request is using the nonce intended for classic AJAX, not a REST wp_rest nonce or vice versa.

The callback runs but the browser cannot parse the response

Look for PHP notices, warnings, debugging output or accidental HTML printed before the JSON. Return responses through wp_send_json_success() and wp_send_json_error(), and inspect the raw Network response. The JavaScript should handle both the structured error branch and a non-JSON transport failure.

A public request is always denied

Review the authorization logic. A callback registered for wp_ajax_nopriv_ still cannot pass a capability check that requires a signed-in user. Decide whether the operation should be authenticated; then either require login and register only the authenticated hook, or build a genuinely public flow with controls appropriate to anonymous traffic.

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 feature works on one site but not another

Check that the endpoint came from admin_url( 'admin-ajax.php' ) rather than a hardcoded root path. Also inspect caching, HTTPS mixed-content errors, script dependencies and whether the file was enqueued on the page where the UI appears.

A practical implementation checklist

  • Load the script with wp_enqueue_script(), or use wp_enqueue_script_module() for a module on WordPress 6.5 or later.
  • Use the correct enqueue hook: wp_enqueue_scripts for the front end and admin_enqueue_scripts for administration pages.
  • Pass the generated endpoint and operation nonce from PHP instead of hardcoding them.
  • Send an action value that exactly matches the registered hook suffix.
  • Register the authenticated and anonymous hook deliberately; do not add wp_ajax_nopriv_ by reflex.
  • Verify the nonce, then perform a capability and ownership check.
  • Unslash, sanitize and validate every request value on the server.
  • Return predictable JSON and handle success, application errors and network failures in JavaScript.
  • Prefer a REST route when the interface represents a reusable WordPress resource or needs external clients.

Bottom line

For a contained theme or plugin interaction, classic WordPress AJAX is straightforward: enqueue the script, localize admin-ajax.php and a nonce, send the matching action, register the correct hook and secure the callback. Treat the nonce as request integrity protection rather than permission, and enforce authorization in PHP. When the feature is a reusable resource API, use a REST route with a wp_rest nonce or an appropriate external authentication method.

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 from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

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.