How to force the browser to reload cached CSS/JS files?

后端 未结 30 4341
青春惊慌失措
青春惊慌失措 2020-11-21 05:49

I have noticed that some browsers (in particular, Firefox and Opera) are very zealous in using cached copies of .css and .js files, even be

相关标签:
30条回答
  • 2020-11-21 05:54

    Update: Rewritten to incorporate suggestions from John Millikin and da5id. This solution is written in PHP, but should be easily adapted to other languages.

    Update 2: Incorporating comments from Nick Johnson that the original .htaccess regex can cause problems with files like json-1.3.js. Solution is to only rewrite if there are exactly 10 digits at the end. (Because 10 digits covers all timestamps from 9/9/2001 to 11/20/2286.)

    First, we use the following rewrite rule in .htaccess:

    RewriteEngine on
    RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]
    

    Now, we write the following PHP function:

    /**
     *  Given a file, i.e. /css/base.css, replaces it with a string containing the
     *  file's mtime, i.e. /css/base.1221534296.css.
     *  
     *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
     *                starting with slash).
     */
    function auto_version($file)
    {
      if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
        return $file;
    
      $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
      return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
    }
    

    Now, wherever you include your CSS, change it from this:

    <link rel="stylesheet" href="/css/base.css" type="text/css" />
    

    To this:

    <link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />
    

    This way, you never have to modify the link tag again, and the user will always see the latest CSS. The browser will be able to cache the CSS file, but when you make any changes to your CSS the browser will see this as a new URL, so it won't use the cached copy.

    This can also work with images, favicons, and JavaScript. Basically anything that is not dynamically generated.

    0 讨论(0)
  • 2020-11-21 05:54

    Dont use foo.css?version=1! Browsers aren't supposed to cache URLs with GET variables. According to http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, though IE and Firefox ignore this, Opera and Safari don't! Instead, use foo.v1234.css, and use rewrite rules to strip out the version number.

    0 讨论(0)
  • 2020-11-21 05:54

    The RewriteRule needs a small update for js or css files that contain a dot notation versioning at the end. E.g. json-1.3.js.

    I added a dot negation class [^.] to the regex so .number. is ignored.

    RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
    
    0 讨论(0)
  • 2020-11-21 05:55

    Simple Client-side Technique

    In general, caching is good.. So there are a couple of techniques, depending on whether you're fixing the problem for yourself as you develop a website, or whether you're trying to control cache in a production environment.

    General visitors to your website won't have the same experience that you're having when you're developing the site. Since the average visitor comes to the site less frequently (maybe only a few times each month, unless you're a Google or hi5 Networks), then they are less likely to have your files in cache, and that may be enough. If you want to force a new version into the browser, you can always add a query string to the request, and bump up the version number when you make major changes:

    <script src="/myJavascript.js?version=4"></script>
    

    This will ensure that everyone gets the new file. It works because the browser looks at the URL of the file to determine whether it has a copy in cache. If your server isn't set up to do anything with the query string, it will be ignored, but the name will look like a new file to the browser.

    On the other hand, if you're developing a website, you don't want to change the version number every time you save a change to your development version. That would be tedious.

    So while you're developing your site, a good trick would be to automatically generate a query string parameter:

    <!-- Development version: -->
    <script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>
    

    Adding a query string to the request is a good way to version a resource, but for a simple website this may be unnecessary. And remember, caching is a good thing.

    It's also worth noting that the browser isn't necessarily stingy about keeping files in cache. Browsers have policies for this sort of thing, and they are usually playing by the rules laid down in the HTTP specification. When a browser makes a request to a server, part of the response is an EXPIRES header.. a date which tells the browser how long it should be kept in cache. The next time the browser comes across a request for the same file, it sees that it has a copy in cache and looks to the EXPIRES date to decide whether it should be used.

    So believe it or not, it's actually your server that is making that browser cache so persistent. You could adjust your server settings and change the EXPIRES headers, but the little technique I've written above is probably a much simpler way for you to go about it. Since caching is good, you usually want to set that date far into the future (a "Far-future Expires Header"), and use the technique described above to force a change.

    If you're interested in more info on HTTP or how these requests are made, a good book is "High Performance Web Sites" by Steve Souders. It's a very good introduction to the subject.

    0 讨论(0)
  • 2020-11-21 05:58

    The 30 or so existing answers are great advice for a circa 2008 website. However, when it comes to a modern, single page application (SPA), it might be time to re-think some fundamental assumptions… specifically the idea that it is desirable for the web server to serve only the single, most recent version of a file.

    Imagine you're a user that has version M of a SPA loaded into your browser:

    1. Your CD pipeline deploys the new version N of the application onto the server
    2. You navigate within the SPA, which sends an XHR to the server to get /some.template
      • (Your browser hasn't refreshed the page, so you're still running version M)
    3. The server responds with the contents of /some.template — do you want it to return version M or N of the template?

    If the format of /some.template changed between versions M and N (or the file was renamed or whatever) you probably don't want version N of the template sent to the browser that's running the old version M of the parser.†

    Web apps run into this issue when two conditions are met:

    • Resources are requested asynchronously sometime after the initial page load
    • The app logic assumes things (that may change in future versions) about resource content

    Once your app needs to serve up multiple versions in parallel, solving caching and "reloading" becomes trivial:

    1. Install all site files into versioned dirs: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
    2. Set HTTP headers to let browsers cache files forever
      • (Or better yet, put everything in a CDN)
    3. Update all <script> and <link> tags, etc. to point to that file in one of the versioned dirs

    That last step sounds tricky, as it could require calling a URL builder for every URL in your server-side or client-side code. Or you could just make clever use of the <base> tag and change the current version in one place.

    † One way around this is to be aggressive about forcing the browser to reload everything when a new version is released. But for the sake of letting any in-progress operations to complete, it may still be easiest to support at least two versions in parallel: v-current and v-previous.

    0 讨论(0)
  • 2020-11-21 06:01

    For development: use a browser setting: eg. Chrome network tab has a disable cache option.

    For production: append a unique query parameter to the request (eg. q?Date.now()) with a server-side rendering framework or pure javascript.

    //pure javascript unique query parameter generation
        //=== myfile.js
        function hello() { console.log('hello') };
        //=== end of file
    
    
    
          <script type="text/javascript">
                document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // document.write is considered bad practice!
                // can't use hello() yet
            </script>')
            <script type="text/javascript">
                hello();
            </script>
    
    0 讨论(0)
提交回复
热议问题