WordPress and Admin AJAX
AJAX is a powerful tool that allows a website to extend its functionality and create a more seamless end-user experience. All AJAX calls in WordPress route through the same location, called Admin AJAX. Learn when not to use Admin AJAX and how to identify which AJAX actions are being made in excess.
About Admin AJAX
AJAX is a JavaScript technology that allows data to be requested and returned on a page without actually refreshing the page. For example, in Google Maps, to view new map sections you don’t need to load another page. As you move the map, it will load in new sections without having to fully reload the page.
By default, WordPress routes all AJAX requests through an admin file located at wp-admin/admin-ajax.php
. This is why, when referring to AJAX in WordPress, we call it “Admin AJAX”.
A common example of Admin AJAX in WordPress would be a checkout page on an ecommerce website. When you enter your shipping information, the site can query the database and display an updated price without reloading the entire page.
High Admin AJAX Usage
When AJAX is called anywhere on a WordPress website, the Admin AJAX file is requested: wp-admin/admin-ajax.php
. On WP Engine, the wp-admin/admin-ajax.php
file is uncached meaning it is generated new for each request and is never served from cache storage. Uncached requests are significantly more resource-intensive than those served from cache.
NOTE
To review how WP Engine server caching works, we suggest checking out this guide.
While AJAX is a great tool, it can often run amok when set to make inefficient, unnecessary or a high number of calls. For this reason, it’s important to use AJAX calls sparingly and only when completely necessary.
For example, say that when a user visits a page it makes 5 Admin AJAX calls within a few seconds. Remember that AJAX calls are uncached, so all 5 must be processed by PHP anew. When 1 or 2 users are on the page, you may not see any impact with only 10 calls in a few seconds. However, when 5,000 visitors are on the page, you now have 25,000 uncached requests within just a few seconds. With enough visitors on the site pulling resources in this inefficient manner, the server will slow down (causing 504 errors) or even fail to complete requests completely (causing 502 errors).
If you’re seeing a large number of 504 or 502 errors on your site, it’s highly possible that excess Admin AJAX requests are the culprit.
NOTE
Need a refresher on these error codes? Learn more about the 502 error here. Learn more about the 504 error here.
Admin AJAX Log
As all AJAX requests go through the wp-admin/admin-ajax.php
file, requests are easy enough to track back to a single culprit with some additional logging enabled. If you’re seeing a large amount of requests to wp-admin/admin-ajax.php
within your access logs, your next step will be to enable Admin AJAX logging.
NOTE
Before continuing, be aware that the Admin AJAX log can grow exponentially fast, which can cause issues for your site. Disable Admin AJAX as soon as possible in your troubleshooting process. We typically find 5-10 minutes to be a sufficient amount of time enabled.
Admin AJAX logging can be enabled by adding the following line to the wp-config.php
file using either SFTP or SSH Gateway. Be sure to add this just below the section that says # WP Engine Settings
.
define( 'WPE_MONITOR_ADMIN_AJAX', true );
Once the line is added and the updated wp-config.php file has been uploaded back into the site, Admin AJAX logging will begin. We suggest timing 5 minutes before editing the file again to remove the line. During this time, perform any actions on your site where you’ve seen slowness or errors, to ensure the process in question is logged during the test period.
To disable Admin AJAX logging, open the wp-config.php
file again and either remove the define above, or update the value to false
.
To review the log, download it at wp-content/__wpe_admin_ajax.log
Finally, be aware that admin AJAX logging only record POST requests. GET requests to admin-ajax.php
with arguments on the end are not logged. For example:admin-ajax.php?action=do_something&that_something=should_be_cool
Troubleshoot Admin AJAX
We recommend troubleshooting high AJAX with SSH gateway, if possible, because we have provided some commands to easily parse the log from your terminal below. This is the same approach WP Engine Support will take to troubleshoot the issue, as it’s the most direct. All of the following commands will be run from the root of your website.
With AJAX logging enabled, view the AJAX actions live as the request is made on the website:
- This is a good option if you are looking for the AJAX call causing slowness on a particular page. Enable AJAX logging, run the following command, then perform the action on the website.
tail -f wp-content/__wpe_admin_ajax.log | grep "action"
With AJAX logging disabled, search through the log to pull a list of actions made and the total times each action was requested during the logged period:
- This is a good option if you’re seeing 502 or 504 errors with high AJAX usage.
grep "action" wp-content/__wpe_admin_ajax.log | sort | uniq -c | sort -rn | head
An example output looks like this:
47608 [action] => adrotate_impression 108 [action] => ga_stats_widget 96 [action] => heartbeat 2 [action] => wp-remove-post-lock 2 [action] => adrotate_click 1 [action] => do-plugin-upgrade
If you’re seeing issues with AJAX performance, the highest number is your most likely culprit. The next step is to determine the offender.
NOTE
The heartbeat action refers to a normal WordPress process that allows for functionality like auto-saving posts and pages. For more information, review the Heartbeat API Codex here. WP Engine disables heartbeat on all non-editing admin pages, so it is unlikely this action will cause any performance issues.
Use the following command to search your website’s files for a specific action name:
ack-grep --nosql action_name
For example, to search a site for the AJAX action adrotate_impression
using the following:
ack-grep --nosql adrotate_impression
This example query was provided the following results:
wp-content/plugins/adrotate/adrotate-statistics.php 40: return "<div class="adrotate_label">" + label + "<br /><span class="adrotate_clicks">Clicks:</span> " + env.opt.values['serie1'][index] + "<br /><span class="adrotate_impressions">Impressions:</span> " + env.opt.values['serie2'][index] + "</div>"; 479: Name: adrotate_impression_callback 483:function adrotate_impression_callback() { wp-content/plugins/adrotate/library/dashboard.css 4:.adrotate_impressions { color: #F80; font-weight: normal } wp-content/plugins/adrotate/library/jquery.adrotate.dyngroup.js 85: $.post(impression_object.ajax_url, {'action': 'adrotate_impression','track': tracker}); wp-content/plugins/adrotate/dashboard/settings/statistics.php 69: <input name="adrotate_impression_timer" type="text" class="search-input" size="5" value="<?php echo $adrotate_config['impression_timer']; ?>" autocomplete="off" /> <?php _e('Seconds.', 'adrotate'); ?><br /> wp-content/plugins/adrotate/adrotate-manage-publisher.php 724: $impression_timer = trim($_POST['adrotate_impression_timer']); wp-content/plugins/adrotate/adrotate.php 68: add_action('wp_ajax_adrotate_impression', 'adrotate_impression_callback'); 69: add_action('wp_ajax_nopriv_adrotate_impression', 'adrotate_impression_callback');
NOTE
We advise creating a backup of your site before making any changes to plugins or themes.
In this example, all the results for search query are located within the plugin directory wp-content/plugins/adrotate
. The next step would be to check for any updates to the Adrotate plugin, and make sure it’s updated. (WP CLI can help with this.) Updating can often resolve issues where an asset’s AJAX usage spikes unexpectedly.
If updating doesn’t resolve the issue, then disabling the item would be the next step. Otherwise, reaching out to the plugin/theme support will ensure you’re able to get the most help, as fast as possible. We recommend providing the developer with your AJAX logs and findings covered above, as this can greatly improve the overall resolution time.
eCommerce Cart Fragments
If you’re seeing a large amount of AJAX calls to wc-ajax=get_refreshed_fragments
this refers to the “Cart Fragments” functionality in WooCommerce. The Cart Fragments request is made all the time, even when completely unnecessary, including pages without add-to-cart functionality, like an About Us page, Blog Posts, or on the Terms & Conditions page. This can pose performance issues, especially in times of high traffic.
You can identify the Cart Fragments on a store with any web page speed test, or the website’s Access Log by adding the following query argument to your domain:
http://testsite.wpengine.com/?wc-ajax=get_refreshed_fragments
The Cart Fragments will appear as follows in your Access Log:
31/Dec/2017:23:59:59 +0000|v1|1.1.1.1|www.testsite.wpengine.com|200|210|127.0.0.1:80|0.328|0.330|POST /?wc-ajax=get_refreshed_fragments HTTP/2.0||
There are two options to resolve high calls from Cart Fragments:
Option 1: Enable Live Cart
Live Cart is a proprietary performance feature built into WP Engine eCommerce Suites that eliminates the AJAX call from happening at unnecessary times, and improves the scalability of the cart and page speed across your entire store without losing dynamic cart functionality. The biggest benefit is seen when items are added to the cart and during high-traffic events, like a special promotion or event (like Black Friday). When a customer adds an item to the cart, Live Cart loads pages on average 1.5 seconds faster than the default Cart Fragments script and helps keep your store from dropped connections under continuous load. Live Cart is not only faster, but it makes the server more stable and responsive in peak traffic.
Live Cart is included with WP Engine eCommerce Suites.
Option 2: Disable Cart Fragments
Disabling Cart Fragments will remove the real-time cart functionality but will reduce overall server load and protect your website’s scalability. There are a few ways of disabling WooCommerce Cart Fragments.
- WP Engine eCommerce suite customers can simply enable Live Cart to maintain the real-time cart while simultaneously improving performance or migrate to HPOS.
- If Live Cart is not available on your account, disable cart fragments using a plugin such as “Disable Cart Fragments” or “Perfmatters” or follow this guide to disable cart fragments via the
functions.php
file of your theme. Either of these options will eliminate dynamic cart functionality.
NEXT STEP: Learn how to reduce “get_refreshed_fragments” Admin AJAX calls