I love admin-ajax.php. But I hate having to localize in order to point frontend scripts to it, and I wish there was an equivalent, easy-to-find file for themes. (It also just bothers me to see frontend requests go through "/wp-admin/". No practical reason, just looks ugly IMO.)

So I've simply copied admin-ajax.php to the root dir at "/ajax.php", adjusted the include paths and removed the WP_ADMIN constant definition. Seems to work like gangbusters (I can now just direct all my frontend AJAX requests to /ajax.php! And I can still use the normal wp_ajax hooks in my plugins!).

But is this safe? What might go wrong? Since this isn't built into core, I assume there's a good reason as to why not. But looking through the code, I can't see any immediate problems.

You're smart--tell me if this approach is crazy. Or if there's a simpler method that I'm overlooking.

  • You might forget about and miss this file during automatic updates, which could result in things breaking and leaving security vulnerabilities in place.
    – Hemm
    May 16, 2013 at 9:13

3 Answers 3


You could just use a RewriteRule to your .htaccess above the regular permalink rewrite rules:

RewriteRule ^ajax$ /wp-admin/admin-ajax.php [L]

Now send your AJAX requests to example.com/ajax, and never miss core changes to that file after upgrades.

  • Great idea! It's one of those things that, after you hear it, you think "that's so simple and obvious!" Thanks.
    – MathSmath
    Jan 29, 2013 at 18:19
  • I added the rewrite rule as per your suggestion but example.com/ajax URL 404's. Could you elaborate on where exactly within the .htaccess should I add this. I have it currently between # BEGIN WordPress <IfModule mod_rewrite.c> and </IfModule> # END WordPress
    – John
    May 9, 2013 at 14:32
  • This works. I was missing the trailing slash. I have permalinks set to /%postname%/
    – John
    May 9, 2013 at 15:30

First: standardization. If you plan on using community plugins, chances are they are not going to care about your /ajax.php file in the document root. So they won't use it.

If you're going to roll everything yourself, this isn't an issue.

Second: what if the core updates? Will you monitor and change your ajax file?

Third: despite admin-ajax.php residing in wp-admin, it does not load any of the admin area stuff (eg. list tables, etc). Nor does it check auth or expose anything sensitive to non-logged in users. It's just like a front-end file, in other words. Nothing to worry about.

Fourth: Related to the first issue, some plugins will check before blindly loading ajax related functionality. An example is below. Your modified ajax.php will likely not cause that to load.

if (is_admin() && defined('DOING_AJAX') && DOING_AJAX) {
    //  load ajax stuff

Finally: What you complain about, using the localization to get the Ajax URL is a good thing to do. Why? Because Your JS files are not aware of any of the server side stuff. You're going to hard a URL in that will break if/when the site moves? Seems like a bad choice.

If you really don't want to localize every script that uses Ajax, simple hook into wp_head really early and spit out the admin ajax URL. Problem solved (this is exactly how the admin area does it, by the way).

add_action('wp_head', 'wpse83650_lazy_ajax', 0, 0);
function wpse83650_lazy_ajax()
    <script type="text/javascript">
    /* <![CDATA[ */
    var ajax_url = "<?php echo esc_js(admin_url('admin-ajax.php')); ?>";
    /* ]]> */
  • Thanks, Chris! All valid points. The biggest thing you helped me realize is that, though I wasn't thinking of this as a "core hack" (since I was adding, not modifying a file), it really is since it depends on other functionality in core that may change. Not too much different from any other plugin (which may also die after changes in core functionality), but definitely philosophically different. Thanks for the thoughts!
    – MathSmath
    Jan 29, 2013 at 18:26
  • Re: "Third: ... Nothing to worry about.", what about when trying to lock-down the wp-admin directory eg using .htaccess rules to limit to known-safe IP ranges? I presume some kind of exception would need to be made for the ajax-admin.php file? (I say "presume" because I'm a numpty when it comes to .htaccess rules, and don't really know if this is possible?).
    – Sepster
    Jul 2, 2013 at 16:18
  • It should be possible to allow a single file, yes. Jul 3, 2013 at 16:43
  • @chrisguitarguy this is a brilliant answer, thank you. I am trying to have admin-ajax loaded on the front end as I am reverse proxying the siteurl to my staging/home url. Please look at this: link Is it possible to do it with 3rd party plugin? Is my approach incorrect?
    – panza
    Jan 21, 2017 at 23:26
  • 1
    Regarding point 3: 10up.github.io/Engineering-Best-Practices/php/#ajax-endpoints ; "WordPress provides an API to register AJAX endpoints on wp-admin/admin-ajax.php. However, WordPress does not cache queries within the administration panel for obvious reasons. Therefore, if you send requests to an admin-ajax.php endpoint, you are bootstrapping WordPress and running un-cached queries. Used properly, this is totally fine. However, this can take down a website if used on the frontend."
    – froger.me
    Jul 16, 2022 at 2:55

As with many things in WordPress, there are a near infinite number of ways to skin the cat. While all of the accepted methods work, I have found that they are less "neat" than using wp_localize_script to include ajax capability on the front end.

Check this out:

add_action( 'wp_enqueue_scripts', 'se83650_js' );
function se83650_js()
    wp_enqueue_script( 'se83650-js', plugin_dir_url( __FILE__ ) . 'js/se83650.js',  'jquery', '1.0.0', true );
    // First param is the name of the script you are attaching it to - in this case
    // it is the name of the custom script we added.  Second param is the name of 
    // the javscript Object that will be attached with your information.
    // Third param is an array of attributes, in this case, ajaxurl
    wp_localize_script( 'se83650-js', 'se83650Ajax', 
            // You can put any variables here you want for your script
            // such as plugin-specific variables or nonces, etc.
            'ajaxurl'    => admin_url( 'admin-ajax.php' )

And then in the se83650.js file, you would reference your variable with se83650Ajax.ajaxurl.

The benefit of this technique is that if you end up with many plugins that try and duplicate this functionality, they aren't including or overwriting the same variable. ajaxurl is pretty generic to include, this gets you more contained and it plays nicer with other developers.

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.