Stop CloudFlare Rocketscript breaking WordPress plugin scripts

This post is more than 10 years old.

CloudFlare offers a service called Rocketscript, which compresses, concatenates, and defers any JavaScript on your web pages. On some websites, it can make a significant difference to the apparent page load time, because it waits until the web browser has loaded the page before it loads and executes scripts. Of course, that sometimes means it breaks things! Fortunately, CloudFlare offers a way to avoid that, and it’s possible to use it on your WordPress plugin scripts.

The trick is to tell CloudFlare that you don’t want your script loaded asynchronously. To do that, you need to add an attribute to the script element, as documented on this CloudFlare support page:

<script data-cfasync="false" src="/javascript.js"></script>

NB: order is very important here — the data-cfasync attribute must come before the src attribute, or CloudFlare will ignore it! No, that’s not very helpful…

Anyway, adding that attribute to the script element for a WordPress script you’ve carefully enqueued using the recommended procedures isn’t as straightforward as you might think. wp_enqueue_script() doesn’t have any way to specify additional script attributes, and WordPress are still talking about how they might allow that.

So the approach I’ve come up with for scripts that spill onto the page footer is to:

  • jump in ahead of the page footer scripts
  • remove my script from the script queue
  • output my own script element, with the special data-cfasync attribute

No doubt you could do the same for scripts that spill onto the page in the header, just by changing the filter to be hooked. So here’s some code that does it:

/**
* output a script tag that won't be replaced by Rocketscript
* @param string $handle
*/
function example_no_rocketscript($handle) {
    global $wp_scripts;

    $script = $wp_scripts->query($handle);
    $src = $script->src;
    if (!empty($script->ver)) {
        $src = add_query_arg('ver', $script->ver, $src);
    }
    $src = esc_url(apply_filters('script_loader_src', $src, $handle));

    echo "<script data-cfasync='false' type='text/javascript' src='$src'></script>\n";
}

/**
* intercept call to print footer scripts, and fix ours
*/
add_action('wp_print_footer_scripts', function() {
    if (wp_script_is('example-script')) {
        wp_dequeue_script('example-script');
        example_no_rocketscript('example-script');
    }
}, 1);

And job is done, at the appropriate time, not when CloudFlare reckons it’s OK to run it. For a real live example, take a look at the little plugin I wrote to stop CloudFlare Rocketscript messing up scripts for my Flexible Map plugin.