I've written a simple plugin that works on a clean install. All it does is once a page is loaded it requests some ajax via admin-ajax.php and then outputs the returned data to the console. When I put on the site I am working which is nothing out of the ordinary it just some custom post types it only works if I am logged in.

When the function that generates the ajax output test_ajax_process_request() is called via wp_ajax_test_ajax when user is logged in it works fine, the console shows what is loaded from admin-ajax.php. But if I am not logged in wp_ajax_nopriv_test_ajax seems to not call test_ajax_process_request(), I know this as I am using error_log to trace when test_ajax_process_request() called. Usually if there is a problem admin-ajax.php will just return a 0 but in this case it's returning my thame's HTML.

My plugin is made of 2 files: ajax_test.php:

Plugin Name:  Simple AJAX test 2018-05-22
Author:       Kevin Meredith
Description:  Pluggin to test client site AJAX request
Version:      1

// disable direct file access
if ( ! defined( 'ABSPATH' ) ) {

function load_js() {

    wp_enqueue_script('ajax_test', plugin_dir_url( __FILE__ ) . 'ajax_test.js', array('jquery'), null, true );

     $test_nonce = wp_create_nonce('test_nonce');
            'ajax_url' => admin_url( 'admin-ajax.php' ),
            'nonce'    => $test_nonce,
    error_log('Load JS TEST SITE');
    // wp_localize_script is used to send an array of php values to javascript
    // it embeds values in the HTML so that be used by java script
add_action( 'wp_enqueue_scripts', 'load_js' );  

function test_ajax_process_request() {
    error_log('test_ajax_process_request() has been called');
    // first check if data is being sent and that it is the data we want
    //check_ajax_referer( $action, $query_arg, $die )
    if ( check_ajax_referer( 'test_nonce', 'js_nonce', false) ) {
        // 1st parameter is the were the nonce is stored in the $_POST array or what the nonce from field name
        // 2nd parameter is the name of the nonce which is set up in wp_create_nonce()
        // 3rd parameter is die, if true and nonce fails they function will return false
        $ran = rand(0, 100);
      $return_string = 'string from JS: "'.$_POST["from_js"].'" random number from server: '.$ran.' nonce: '.$_POST["js_nonce"].' TEST SITE';
      echo $return_string;
      $return_string = 'nonce fail (ajax_test.php)';
        echo ($return_string);
add_action('wp_ajax_test_ajax', 'test_ajax_process_request');
add_action('wp_ajax_nopriv_test_ajax', 'test_ajax_process_request');

And my JS file: ajax_test.php

var $ = jQuery.noConflict();

$( document ).ready(function() {

    console.log('Nonce: '+wbs_ajax_obj.nonce+' from ajax_test.js');

      url: wbs_ajax_obj.ajax_url, // 'https://lomokev.com/ajaxtest'
      data: {
          action: "test_ajax", // name of the wordpress hook that will trigger function so in this case wp_ajax_nopriv_test_ajax and wp_ajax_test_ajax'
          js_nonce: wbs_ajax_obj.nonce,
          from_js: 'value of from_js from ajax_test.js'
      error: function(errorThrown){


    // if it's a 404 the error says "error"
    // but this says "Bad Request"


out from logged in and none logged in user

  • Have you considered using a REST API endpoint instead? They're much easier to debug, and much more secure
    – Tom J Nowell
    May 22, 2018 at 14:13
  • I’ve also come to that conclusion myself it's just a little annoying because spent time learning how to do it this way ;-).
    – lomokev
    May 22, 2018 at 15:02
  • Is there a chance that some other plugin already registered action wp_ajax_nopriv_test_ajax? It's a pretty common name, so there is such chance, I guess. In such case the response can be generated not by your function. You can change priority of your hook. May 23, 2018 at 5:45
  • 1
    @KrzysiekDróżdż I did it on a clean install so the hook was not clashing, that said it's a really good point test_ajax is an obvious name for ajax hook ;-) I did figure it out I had some code that was redirecting none logged in users away from the wp-admin which would include admin-ajax.php see my answer below if your interested.
    – lomokev
    May 23, 2018 at 20:53

1 Answer 1


Found the solution, reason why it was returning HTML is because it was doing a 302 redirect to the home page.

Turns out that I had a function in my theme that redirects none logged in users away from the wp-admin pages which is where admin-ajax.php is.

    // IF SUSBSCIBER GOES TO ADMIN /wp-admin THEY ARE REDIRCTED TO THE HOME PAGE START : this code works as of 2018-01-29 (no plugins)

      * Redirect back to homepage and not allow access to
      * WP admin for Subscribers.

     function themeblvd_redirect_admin(){
          if ( ! current_user_can('delete_published_posts')) { // changed from edit_posts so that it works for contibuters to
               wp_redirect( site_url() );
     add_action( 'admin_init', 'themeblvd_redirect_admin' );

I have it there for testing, so if have testing with a holding page I allow clients to login using a subscriber account, once they are logged in they are redirected to the home page instead of seeing the there user page. I found the solution for this once I released it was a 302 redirect, I found this thread on the subject: https://stackoverflow.com/questions/9408334/wordpress-admin-ajax-results-in-error-302-redirect

  • This answer saved me hours. Had a very opinionated plugin that decided it knew what was best, this answer helped me find it and choose an alternative.
    – Ian
    Jul 12, 2022 at 20:21
  • @Ian Glad it helped.
    – lomokev
    Jul 14, 2022 at 13:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.