I just setup W3 Total Cache S3 CDN (origin push) and most of the URLs are being rewritten correctly but we have some custom plugins where the URLs are not being rewritten. How do I grab the rewritten URL of W3TC from another plugin? I'm currently using the following code to grab the image:

wp_get_attachment_image_src( get_post_thumbnail_id(), 'car-thumb');

I'm assuming there is a filter of some sort that just replaces the original "origin" url with the asset URL on the S3 bucket but I've been unable to find where in the code.

closed as off-topic by mrwweb, Nicolai, birgire, Pieter Goosen, Johannes Pille Aug 29 '14 at 12:22

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Your question should be specific to WordPress. Generic PHP/JS/HTML/CSS questions might be better asked at Stack Overflow or another appropriate site of the Stack Exchange network. Third party plugins and themes are off topic." – mrwweb, Nicolai, birgire, Pieter Goosen, Johannes Pille
If this question can be reworded to fit the rules in the help center, please edit the question.

  • did you get this to work somehow? – pkyeck Jun 26 '13 at 20:57
  • 4
    Another wildly inaccurate off-topic closure. "This party plugins and themes are off topic" is a great way to make this stack exchange site irrelevant. – El Yobo Jun 19 '15 at 2:20
  • 1
    I agree with @ElYobo, it's not like a WordPress plugin will ever be used outside of WordPress, so how could it possibly be better off in a more general forum like Stack Overflow? It might make sense if it was just a general PHP question, but if it's about a plugin, and WordPress functions, that's very different. – Ryan Taylor Nov 6 '15 at 18:59

The W3 Total Cache plugin changes the URL of various files in /w3-total-cache/lib/W3/Plugin/Cdn.php in the function *ob_callback*. It uses a series of callbacks to modify an output buffer. The code runs like this:

  • w3_total_cache.php calls $root->run();
  • W3_Root::run calls $plugin->run() for each plugin in $this->_loaded_plugins
  • W3_Plugin_TotalCache::run starts an output buffer which calls W3_Plugin_TotalCache::ob_callback
  • W3_Plugin_TotalCache::ob_callback calls w3tc_do_ob_callbacks() which runs any callback stored in $GLOBALS['_w3tc_ob_callbacks']
  • The CDN adds it's own callback to that global in W3_Plugin_Cdn::run. That callback is W3_Plugin_Cdn::ob_callback

Which callbacks then run is unfortunately hard coded in this line: $buffer = w3tc_do_ob_callbacks(array('minify', 'newrelic', 'cdn', 'browsercache', 'pagecache'), $buffer);

Because this is hard coded, if you ever need to modify what is included and what isn't, you'll have to change their callback.


I have a plugin that exports JSON, and the CDN aspect of W3 Total Cache wasn't changing any URLs for JSON requests. It turns out that my output was failing the w3_is_xml($buffer) test.

I fixed it by turning their single CDN callback into multiples, like this:

// Modify the output buffer callbacks of W3 Total Cache to work with the JSON API
if (!empty($GLOBALS['_w3tc_ob_callbacks']) && isset($GLOBALS['_w3tc_ob_callbacks']['cdn'])) {
    // Back-up the original value of $GLOBALS['_w3tc_ob_callbacks']['cdn']
    // This should be W3_Plugin_Cdn::ob_callback
    $this->cdn_ob_callback = $GLOBALS['_w3tc_ob_callbacks']['cdn'];

    // Replace $GLOBALS['_w3tc_ob_callbacks']['cdn'] with out own method
    // which will call the original callback in between two of our own
    $GLOBALS['_w3tc_ob_callbacks']['cdn'] = array($this, 'do_multiple_cdn_ob_callbacks');

Then doing the changes I need, making sure to call their original callback in the middle.

public function do_multiple_cdn_ob_callbacks(&$buffer) {
    // Frist run our own callback to add an XML string to the buffer
    // so that the content passes the w3_is_xml($buffer) test
    $buffer = $this->w3_total_cache_ob_callback_start($buffer);

    // Next run the original callback, which will replace the asset URLs
    $buffer = call_user_func($this->cdn_ob_callback, $buffer);

    // Finally, run another callback of our own to remove the XML string
    $buffer = $this->w3_total_cache_ob_callback_end($buffer);
    return $buffer;

I had a similar issue when rendering a template part.

I had to invoke the W3_Plugin_Cdn plugin programmatically.

As correctly stated by Tyler V., the w3_is_xml($buffer) will fail if $buffer doesn't look like HTML or XML, that's why I'm putting <html> at the beginning of the template part.

Here's an example on how I did.

if (class_exists('W3_Plugin_Cdn')) {
    $cdn_handler = new W3_Plugin_Cdn();
    echo '<html>';
    get_template_part($templatepart, $templatepartdetail);
    $buffer = ob_get_contents();
    $buffer = substr($buffer, strlen('<html>'));
    echo $buffer;

I had the same issues and couldn't get an answer either. I did however find where in the database WP is storing the entries for the attached images and used that to create my own rewritten URLs.

If you look in the _postmeta table there is a meta_key "_wp_attached_file" that stores the folder path to the attached images. With this information I only needed to add the URL structure for where our S3 bucket is located.

global $postid;
$bucketName = "your_bucket_name_goes_here";
$postThumbNailID = get_post_meta($post->ID, '_thumbnail_id');
$postThumbNailUrl = get_post_meta($postThumbNailID[0], '_wp_attached_file');
$s3BucketURL = "http://".$bucketName.".s3.amazonaws.com/files/".$postThumbNailUrl[0];
  • 1
    Where did you put this code? This doesn't look like a complete solution to me. – s_ha_dum Feb 14 '13 at 20:52

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