Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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.

share|improve this question
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 '13 at 9:13
add comment

3 Answers

up vote 13 down vote accepted

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.

share|improve this answer
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 '13 at 18:19
@toscho 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 '13 at 14:32
@toscho This works. I was missing the trailing slash. I have permalinks set to /%postname%/ –  John May 9 '13 at 15:30
add comment

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')); ?>";
    /* ]]> */
share|improve this answer
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 '13 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 '13 at 16:18
It should be possible to allow a single file, yes. –  chrisguitarguy Jul 3 '13 at 16:43
add comment

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.

share|improve this answer
add comment

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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