iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Antihost offers managed WordPress hosting with free migration, one-click staging and a 14-day free trial.
To use AJAX in WordPress, enqueue a JavaScript file, pass it the site’s generated admin-ajax.php URL and a nonce, send an action value, and register the matching PHP hooks. Verify the nonce, authorize the operation, validate input, and return JSON. Use a REST API route instead when the feature represents a reusable WordPress resource or must serve clients beyond one page.
What AJAX does in WordPress
AJAX lets a browser send a request and update part of the current page without navigating away or downloading the whole document again. WordPress commonly returns JSON, even though the historical acronym expands to Asynchronous JavaScript and XML. A search filter, voting button, inline form, cart update, or “load more” control can therefore change only its result area.
There are two practical WordPress approaches:
- Classic AJAX: send a request to the site-specific
wp-admin/admin-ajax.phpendpoint. WordPress reads the request’sactionand runs a matching PHP hook. - REST API: register a route such as
/wp-json/my-plugin/v1/value. This models a resource or operation as an endpoint that can be reused by several pages, applications, or integrations.
For a small interaction in a theme or plugin, classic AJAX is usually the shortest implementation. For a durable, resource-oriented interface, a REST route gives you clearer URLs, HTTP methods, and reuse.
How a classic WordPress AJAX request works
- PHP enqueues the JavaScript file on the page where it is needed.
- PHP passes the browser the complete AJAX URL and a nonce. The URL can differ between installations, subdirectory sites, or unusual WordPress setups, so do not hardcode it in JavaScript.
- The browser sends the URL, an
action, the nonce, and any form values. - WordPress maps that action to
wp_ajax_<action>for logged-in users orwp_ajax_nopriv_<action>for logged-out visitors. - The callback verifies the nonce, checks permission where appropriate, validates and sanitizes input, performs the operation, and sends a JSON success or error response.
- JavaScript checks the response and updates the relevant DOM element.
The action string is the connection between JavaScript and PHP. A spelling difference, extra character, or wrong logged-in hook prevents the intended callback from running.
#1 Best Overall
Build a working classic AJAX feature
1. Enqueue the script and pass configuration
Put this in a plugin or in the theme code that owns the feature. wp_enqueue_script() lets WordPress manage dependencies, ordering, and loading. The final argument loads the file in the footer.
<?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 small JavaScript object before the enqueued file. In this example, the browser receives myAjax.ajaxUrl and myAjax.nonce. The nonce action, my_plugin_action, must be the same value checked by the PHP callback.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use a unique script handle and a version that changes when the file changes. If the file belongs to a plugin, plugin_dir_url( __FILE__ ) is suitable when used from that plugin file. A theme can instead use its theme directory URL. Do not put a fixed /wp-admin/admin-ajax.php string in the JavaScript source.
2. Add the page markup
The browser needs a control to trigger the request and an element to receive the result. The markup can be rendered by a shortcode, block, template, or plugin view.
<label for='value'>Value</label>
<input id='value' type='text'>
<button id='send-value' type='button'>Send</button>
<p id='result' aria-live='polite'></p>
An aria-live region lets assistive technology announce the asynchronous result. Disable the button while a request is in flight if duplicate submissions would be harmful.
3. Send the request with jQuery
If the script declares jQuery as a dependency, jQuery.post() is a concise option:
Rank #2
jQuery(function ($) {
$('#send-value').on('click', function () {
$.post(
myAjax.ajaxUrl,
{
action: 'my_plugin_action',
_ajax_nonce: myAjax.nonce,
value: $('#value').val()
}
)
.done(function (response) {
if (response.success) {
$('#result').text(response.data.message);
} else {
$('#result').text(response.data.message || 'The request failed.');
}
})
.fail(function () {
$('#result').text('The server could not be reached.');
});
});
});
The request is a POST, its action is my_plugin_action, and the nonce is sent in _ajax_nonce. WordPress’s JSON helpers produce a response with a Boolean success property and a data property.
4. Use fetch instead of jQuery when preferred
Modern JavaScript does not require jQuery. Send form-encoded data so PHP receives the values in $_POST:
document.querySelector('#send-value').addEventListener('click', async function () {
const body = new URLSearchParams({
action: 'my_plugin_action',
_ajax_nonce: myAjax.nonce,
value: document.querySelector('#value').value
});
const result = document.querySelector('#result');
try {
const response = await fetch(myAjax.ajaxUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body
});
const json = await response.json();
result.textContent = json.success
? json.data.message
: (json.data && json.data.message) || 'The request failed.';
} catch (error) {
result.textContent = 'The server could not be reached.';
}
});
Whether you use jQuery or fetch(), the endpoint, action, nonce, and response contract remain the same. Check the HTTP status as well as the JSON body when your callback sends an error status.
5. Register the PHP hooks and callback
Register both hooks only when the feature is deliberately available to both authenticated and anonymous visitors. The callback below demonstrates a public operation that still verifies a nonce and returns structured JSON.
<?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' );
$value = isset( $_POST['value'] )
? sanitize_text_field( wp_unslash( $_POST['value'] ) )
: '';
if ( $value === '' ) {
wp_send_json_error(
array( 'message' => 'A value is required.' ),
400
);
}
wp_send_json_success(
array( 'message' => 'Received: ' . $value )
);
}
check_ajax_referer() stops the callback when the nonce is absent, stale, or created for another action. wp_unslash() removes WordPress’s request slashes before sanitization. sanitize_text_field() is suitable for a simple text value; it is not a universal sanitizer for HTML, URLs, email addresses, numbers, or structured data.
For a private action, omit the wp_ajax_nopriv_my_plugin_action hook and require a capability:
Rank #3
function my_private_action() {
check_ajax_referer( 'my_private_action' );
if ( ! current_user_can( 'edit_posts' ) ) {
wp_send_json_error(
array( 'message' => 'Permission denied.' ),
403
);
}
// Validate the submitted values, perform the operation, and respond.
}
A nonce is not authentication and does not grant a user permission. A logged-in user can possess a valid nonce and still lack the capability required to edit, delete, publish, or view protected data. Choose the narrowest capability that matches the operation. For a public action, do not add a capability test that every anonymous visitor will fail; instead, make sure the operation itself is safe for public use and apply validation and abuse controls appropriate to it.
Nonces, authorization, and input safety
Use the same nonce action at both ends
Create the nonce with wp_create_nonce( 'my_plugin_action' ), send it as _ajax_nonce (or another field explicitly supplied to the verifier), and call check_ajax_referer( 'my_plugin_action' ). A mismatch is a common cause of a “-1” response or an error before your callback’s main code runs.
Recommended Free Tools
Do not treat a nonce as a login check
Use current_user_can() or an equivalent authorization rule for protected operations. The capability should be checked after nonce verification and before changing data. For object-specific permissions, also verify that the current user may access the particular post, term, order, or record identified by the request.
Validate, sanitize, and escape for their different jobs
- Validate: confirm that a value has the expected type, range, format, and relationship to other values. Reject an unknown post ID or an unsupported sort direction rather than silently accepting it.
- Sanitize: clean data before storing or processing it. Use a sanitizer that matches the data type.
- Escape: escape output for its destination. Text inserted with
textContentdoes not need HTML parsing; output printed into HTML, attributes, JavaScript, or a URL needs the corresponding WordPress escaping function.
Never concatenate untrusted input into SQL, shell commands, HTML, or JavaScript. Use WordPress APIs and prepared database queries where a database operation is required.
Return predictable JSON
Use wp_send_json_success() for successful results and wp_send_json_error() for failures. Put machine-readable values and a human-readable message in the response data:
wp_send_json_success(
array(
'message' => 'Saved.',
'itemId' => $item_id,
)
);
wp_send_json_error(
array(
'message' => 'The item could not be saved.',
'code' => 'save_failed',
),
500
);
Keep the shape stable so the client can handle it without guessing whether a field exists. Handle both an application-level failure (success: false) and a transport failure such as a timeout, malformed response, or server error. Do not print notices, debugging text, or a PHP warning before the JSON response; stray output can make an otherwise valid response impossible to parse.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #4
Enqueue AJAX code only where it is needed
Front-end interactions normally use wp_enqueue_scripts. For an administration page, use admin_enqueue_scripts and inspect the supplied screen hook before loading the file:
<?php
add_action( 'admin_enqueue_scripts', function ( $hook_suffix ) {
if ( 'toplevel_page_my-plugin' !== $hook_suffix ) {
return;
}
wp_enqueue_script(
'my-plugin-admin-ajax',
plugin_dir_url( __FILE__ ) . 'assets/admin-ajax.js',
array(),
'1.0.0',
true
);
} );
Loading an interaction on every public page increases work and can create conflicts with unrelated scripts. Keep the localization object immediately associated with the script that consumes it.
Since WordPress 6.5, script modules can be loaded with wp_enqueue_script_module(). Modules are useful when your code uses native import and export; the AJAX protocol still requires the same generated endpoint, action, nonce, and response handling.
When to use the REST API instead
Choose a REST route when the feature maps naturally to a resource such as posts, profiles, settings, or a custom collection, or when several pages and clients should consume the same endpoint. A route describes a URL and HTTP method instead of hiding every operation behind one endpoint and an action string.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Register a route
<?php
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' );
},
)
);
} );
function my_plugin_rest_value( WP_REST_Request $request ) {
$value = sanitize_text_field( $request->get_param( 'value' ) );
if ( $value === '' ) {
return new WP_Error(
'empty_value',
'A value is required.',
array( 'status' => 400 )
);
}
return rest_ensure_response(
array( 'message' => 'Received: ' . $value )
);
}
A REST route must include a permission_callback. Return a WP_Error with a status when the request is invalid or unauthorized. Public routes can return true from the permission callback only when the resource and operation are intentionally public and otherwise protected against abuse.
Authenticate a browser request
For a cookie-authenticated request made by WordPress’s own logged-in browser, create a nonce with the wp_rest action and send it in the X-WP-Nonce header. The nonce may also be supplied as the _wpnonce parameter.
Best Value
wp_localize_script(
'my-plugin-rest',
'myRest',
array(
'root' => esc_url_raw( rest_url( 'my-plugin/v1/' ) ),
'nonce' => wp_create_nonce( 'wp_rest' ),
)
);
const response = await fetch(myRest.root + 'value', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': myRest.nonce
},
body: JSON.stringify({ value: 'Example' })
});
const data = await response.json();
For authenticated external REST calls that cannot use a WordPress browser cookie, Application Passwords are available in WordPress 5.6 and later. Use an authentication method appropriate to the client; never expose a long-lived credential in front-end source.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Classic AJAX versus REST routes
| Consideration | admin-ajax.php |
REST API route |
|---|---|---|
| Endpoint model | One site-specific endpoint plus an action that selects a PHP hook. |
A named route representing a resource or operation, normally under /wp-json/. |
| Best fit | A focused theme or plugin interaction tied to one page. | A reusable interface for posts, custom data, settings, or multiple clients. |
| Browser nonce | Create an action nonce and verify it with check_ajax_referer(). |
Create a wp_rest nonce and send X-WP-Nonce or _wpnonce. |
| Authorization | Check capabilities in the callback; the nonce is not permission. | Implement authorization in permission_callback and, when needed, in the callback for the specific resource. |
| Response contract | Commonly wp_send_json_success() and wp_send_json_error() with a success flag. |
REST responses and WP_Error use HTTP status codes and resource-oriented JSON. |
| Logged-out support | Register the separate wp_ajax_nopriv_ hook deliberately. |
Expose a public permission callback only when the route is safe for anonymous access. |
| Reuse outside the current page | Possible, but consumers must know the action and payload convention. | Usually clearer for JavaScript applications, mobile clients, and integrations. |
There is no requirement to migrate every small interaction to REST. Choose the interface whose security model and payload shape your feature can maintain. Avoid creating two implementations for the same operation unless different clients genuinely need them.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesDebugging when WordPress AJAX does not work
The callback never runs
- Open the browser’s Network panel and confirm the request URL is the localized
admin-ajax.phpvalue, not a guessed path. - Check that the request contains an
actionfield. - Compare the action character-for-character with the suffix in
wp_ajax_<action>orwp_ajax_nopriv_<action>. - Determine whether the browser is logged in. A logged-out request will not use the logged-in hook, and a logged-in request will not be fixed by adding only the anonymous hook.
- Confirm that the PHP file containing the hook is loaded before the request arrives and that the callback name is spelled correctly.
The response is “-1” or reports a nonce failure
Regenerate the nonce through the enqueue and localization path, make sure the JavaScript sends it under the field checked by check_ajax_referer(), and verify that the nonce action string is identical on creation and verification. A cached page can serve an old nonce; exclude nonce-bearing pages or refresh the value through an appropriate WordPress request strategy.
The browser reports invalid JSON
Inspect the raw response. PHP notices, warnings, accidental whitespace, template output, or a debugging echo before wp_send_json_*() will corrupt JSON. Fix the underlying PHP notice and ensure every execution path ends with one JSON response. Also confirm the client is not trying to parse an HTML login page or server error as JSON.
The request succeeds but nothing changes
- Log or inspect the parsed response and verify the callback returned the field your JavaScript reads, such as
response.data.message. - Confirm the selector points to an element that exists after the page renders.
- Use
textContentor jQuery’stext()for plain text; use controlled, escaped markup when HTML is genuinely required. - Check that a second click is not replacing the first result or that a failed promise is not being ignored.
Administration code loads on the wrong screens
Use admin_enqueue_scripts, inspect the screen hook, and enqueue only on the page that owns the control. Verify the script’s dependencies and load order in the page source and Network panel.
Performance and reliability choices
- Send only the fields required for the operation and return only the data the interface needs.
- Debounce high-frequency actions such as live search so a request is not sent for every keystroke.
- Show a loading state, prevent duplicate submissions when necessary, and restore the control after success or failure.
- Design the callback to be repeat-safe when users can retry. A repeated payment, deletion, or state transition needs server-side safeguards; disabling a button alone is not protection.
- Use pagination or bounded limits for lists. Do not let an anonymous request ask the server for an unbounded result set.
- Keep error messages useful to the user but avoid returning secrets, stack traces, database details, or capability information that helps an attacker.
A practical decision checklist
- Does the interaction update one page area without a full navigation? AJAX is appropriate.
- Is a short-lived theme or plugin action all you need? Start with
admin-ajax.php. - Will several pages, applications, or future integrations consume the same data? Prefer a REST route.
- Is the action public? Register the anonymous hook or public REST permission deliberately, and make the operation safe without a user capability.
- Is it private or destructive? Verify a nonce, check the exact capability and object permission, validate every field, and register only the authenticated path.
- Can you see the generated URL, action, nonce, HTTP status, and JSON body in the Network panel? Those five observations usually identify the failing layer.
Conclusion
A reliable WordPress AJAX feature is a small, explicit contract: PHP supplies the correct endpoint and nonce, JavaScript sends the action and validated data, WordPress invokes the matching logged-in or anonymous hook, and the callback authorizes the operation before returning predictable JSON. Use that classic flow for a focused page interaction; choose a REST route when the data deserves a reusable resource API.
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 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteQuick 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.

