65

Suppose that we changed a lot of functionality for the module (templates, layouts, CSS) and we are going to move these changes to the production site, but a lot of customers have cached CSS in their browsers. So here is a question. How to force flush client's CSS cache and avoid file renaming (styles.css -> styles-v2.css). There is one logical way but it doesn't work in Magento, because it checks file existing (by the way this method works for JS files), see below:

<action method="addCss">
    <stylesheet>css/styles.css?1</stylesheet>
</action>  

Any ideas?

15 Answers 15

38

One way of dealing with this is enabling merging of CSS. Then you could just clear the cache and a new merged file would be created with a new file name.

System -> Configuration -> Developer -> CSS settings -> Merge CSS Files

As far as I know, the hash code of the merged CSS file stays the same even if the underlying files changed - only if new files are added to the set of merged files, the hash changes. -- @Alex

Another way of dealing with this is instead of using the layout.xml,

just put them in your page/html/head.phtml

Or create a block that contains a <style> tag with version number and put it in the XML in your head so you can have it load on only specific pages and still sticking to using the XML layouts.

6
  • 11
    As far as I know, the hash code of the merged CSS file stays the same even if the underlying files changed - only if new files are added to the set of merged files, the hash changes.
    – Alex
    Commented Jan 23, 2013 at 8:27
  • @Alex didn't know that, makes sense. Commented Jan 23, 2013 at 8:28
  • 4
    I haven't looked at this recently, but in the past the compilation of CSS / JS seems to actually add extra 'weight' to your site if you load different CSS / JS on different pages. It created a different compiled version per unique set of scripts. This means larger files that get compiled in are essentially being downloaded multiple times. Commented Jan 23, 2013 at 9:02
  • @cags - Yep, basically, under these conditions, minifying and allowing all CSS/JS files to download once is the only speed enhancement that works. Commented Jun 15, 2013 at 19:45
  • This can sometimes change CSS behavior, at least for me in Magento 1.9.2.1
    – Goose
    Commented Apr 7, 2016 at 19:48
19

You can use the OpenSource Module Aoe_JsCssTstamp which adds timestamp information to the merged CSS files. Timestamps for plain (un-merged) CSS files are not yet supported however but this would be easy to implement.

10

There is a free extension on github 'Magento Cachebuster' that does exactly this. It re

https://github.com/gknoppe-guidance/magento-cachebuster

The module provides cachebusting by automatically altering the URI created by Magento for >static files by adding the timestamp of the file to the filename:

Before: http://www.example.com/js/varien/js.js After: http://www.example.com/js/varien/js.1324429472.js

1
  • 2
    This module parses the HTML for each response to add the timestamps, which could potentially hurt performance. github.com/fbrnc/Aoe_JsCssTstamp does the same in a more performant way, but it needs to rewrite the design package model to accomplish it, while Cachebuster only uses an observer. Commented May 21, 2015 at 7:39
10

I use my own extension Speedster Advanced for this. But the basic principle is that the name of the merged css and js files includes the timestamp of the last modified file - see Mage_Core_Model_Design_Package::getMergedCssUrl(). Any time you edit any of the css files a new file name is created causing browsers to request the new file instead of reusing the cached version. Since your head block might be cached a Magento cache refresh is needed.

1
  • Fooman Speedster gets my up vote great extension
    – Bobadevv
    Commented Apr 7, 2016 at 21:16
9

I also have implemented a cache buster for css files. Best way I guess is to extend Mage_Page_Block_Html_Head and over ride the function below and update $skinItems array with your desired changes.

protected function &_prepareStaticAndSkinElements($format, array $staticItems, array $skinItems, $mergeCallback = null)
{
    $designPackage = Mage::getDesign();
    //$skinItems: contains all css
    foreach ($skinItems as $params => $rows) {
        foreach ($rows as $key=>$name) {
            $file = $designPackage->getFilename($name, array('_type' => 'skin'));
            $skinItems[$params][$key] = $name . "?fmt=" . filemtime($file);
        }
    }
    return parent::_prepareStaticAndSkinElements($format, $staticItems, $skinItems, $mergeCallback);

}

Got the inspiration from here. Source

4
  • 1
    This won't work, the skin files will always fall back to base/default because the filename won't be found with the appended query string.
    – BlueC
    Commented Sep 21, 2016 at 13:09
  • your comments "The filename won't be found with the appended query string" thats what we want and thats what will bust the cache and force the cache server to fetch a fresh copy.
    – Ahad Ali
    Commented Sep 22, 2016 at 23:30
  • 1
    No, that isn't how it works at all. You are changing the values of the elements in the $skinItems array and then passing those back to the parent _prepareStaticAndSkinElements() method. This parent method will call Mage::getDesign()->getSkinUrl() on each modified item which will then always fallback to base/default because it can't locate the files with the appended ?fmt=xxx on the filesystem.
    – BlueC
    Commented Sep 23, 2016 at 7:54
  • Not sure about his implementation, but the inspiration sourced at the bottom definitely works exactly as you'd hope, github.com/mklooss/Loewenstark_Head
    – Goose
    Commented Aug 25, 2017 at 17:52
8

There is a simple but cumbersome workaround that doesn't require any plugins and just uses built in Magento capabilities - useful if you just have to quickly do it on an existing site without wanting to risk installing any more code.

The idea is that you can use the merged CSS system to generate a cache busting file name.

As the merged CSS file name is a hash of all the files that are merged together you simply add an extra blank css file into the theme with a date stamp for a name.

So:

  1. Turn on Merge CSS files in Configuration > Advanced > Developer
  2. In your theme layouts find where you add CSS files to the head (typically page.xml) and add an extra stylesheet file, call it anything you want as long as the name is unique eg <action method="addCss"><stylesheet>css/cachebust_091014.css</stylesheet></action>
  3. In your skin CSS folder create a new css file with that name, for the file contents I just put a comment saying what the file is for

Now push that live and flush the magento cache, the merged css file will now have a different name and your caches will be busted!

It's cumbersome as each time you want to bust the cache you need to change that file name, but it requires nothing other than built in Magento capabilities so it's handy if you find yourself stuck and need a quick fix!

7

=> Instead of using this code:

<action method="addCss">
    <stylesheet>css/styles.css?1</stylesheet>
</action>

=> Try to use this code :

<reference name="head">
    <block type="core/text" name="foocss">
        <action method="setText">
            <css><![CDATA[<link rel="stylesheet" type="text/css" href="foo.css?1" media="all" />]]></css>
        </action>
    </block>
</reference>

But it's not very nice...

2
  • Interesting idea :)
    – Nick
    Commented Jan 23, 2013 at 8:56
  • This is an excellent idea for a short term check. Commented May 7, 2015 at 15:31
4

I found a module that will append a query string to the end of all CSS and JS in xml layouts. The query string is configurable from the admin.

https://github.com/mklooss/Loewenstark_Head

The basic idea is to override _prepareStaticAndSkinElements to include a query string, as done in the module, shown below.

protected function &_prepareStaticAndSkinElements($format, array $staticItems, array $skinItems, $mergeCallback = null)
{
    $version = Mage::getStoreConfig("design/head/meta_version_tag");
    $format = sprintf($format, "%s?v{$version}", "%s");
    return parent::_prepareStaticAndSkinElements($format, $staticItems, $skinItems, $mergeCallback);
}
3

If I understand the proposed solution in your question, you can do that with a slight mod to a core file (don't actually edit the core file):

Mage/Page/Block/Html/Head.php

Add something like ?v=1 to line 198 so all css files have this appended:

$html .= $this->_prepareStaticAndSkinElements('<link rel="stylesheet" type="text/css" href="%s?v=1"%s />' . "\n",
3

I think Fooman Speedster Advanced (http://www.magentocommerce.com/magento-connect/fooman-speedsteradvanced-4030.html) could be a solution:

"Fully automated once installed, with automatic versioning when Javascript/CSS files are updated"

2

I have built a free module for this:

http://www.magentocommerce.com/magento-connect/frontend-flush-2048.html

Please let me know if it does not work as expected but I've built it so that the combined js and css files will have a different hash if the content of one of the concatenated files has changed. By default Magento only changes the hash of the combined file if the filename of one of the included files has changed.

UPDATE

I also made a free and simple minify module for the ones of you who believe in it.

http://www.magentocommerce.com/magento-connect/minify-7771.html

1
  • This module is not working...
    – SIBHI S
    Commented Mar 20, 2015 at 9:12
2

There's a really nice module created by Fabrizio Branca that does exactly the thing that you're interested in. It's called AOE_JsCSSTStamp . What it does? It adds a time stamp to both CSS and JS resources. When you flush CSS/JS cache then the timestamps are recreated.

The browser will see different filename - that's why it will redownload the resources again and be served with the freshest one instead of cached in the browser.

1

Just edit the method getCssJsHtml in Mage_Page_Block_Html_Head, add a string like this for a few days after css edit and that's all... it simply works

// static and skin css
        $html .= $this->_prepareStaticAndSkinElements('<link rel="stylesheet" type="text/css" href="%s?foo=WHAT_YOU_WANT_HERE"%s />' . "\n",
            empty($items['js_css']) ? [] : $items['js_css'],
            empty($items['skin_css']) ? [] : $items['skin_css'],
            $shouldMergeCss ? [Mage::getDesign(), 'getMergedCssUrl'] : null
        );
1

Few years later and not finding any useful extension that won't merge files and is simple, I created my own. The main idea is that after flushing cache, it will update the Timestamp. So in other words - when you change some css/js, just flush cache and Timestamp will be updated.

Source code is here -> https://github.com/archonkulis/ANSolutions_CssJsTimestamp

Works on 1.9+ version. Not sure about older versions, though, but most likely should work as well.

-3

Make a copy of your theme with a new name (themev2) - both skin and app/design, etc. Then choose the new theme in admin.

7
  • no, you never do that. that's really a bad way of doing it
    – Marius
    Commented Aug 25, 2014 at 20:43
  • Why not? This way if something goes wrong with the new version, you can quickly go back to the old version. If you are using long browser-caching times and/or CDNs to serve your css (and js which might also need to be flushed/invalidated), this is by far the easiest way. Commented Aug 25, 2014 at 21:27
  • If something goes wrong, you roll back, which should include another (the old) filename, therefore changing the configuration (read as package/theme) is not needed Commented Aug 25, 2014 at 22:00
  • I don't know how you do deployments, but this way I have to keep the old theme folder until I change the value for package/theme, or create a script that updates the value on install. Also, if I have different themes set for different periods of time they might be affected. Duplicating a lot of files is bot the easiest way by far. For example installing this: github.com/jreinke/magento-suffix-static-files is much easier. All you have to do is go an change a number in the backend after each deployment.
    – Marius
    Commented Aug 26, 2014 at 10:23
  • Don't even think like that!
    – Duke
    Commented Apr 13, 2015 at 6:17

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.