开发者

How to set up caching for css/js static properly

to prevent problem, when I update CSS/JS media files and browsers dont request new version, because they cache these files I used this solution: https://github.com/jaddison/django-cachebuster, that adds ?<timestamp of file> to CSS/JS filenames (replaces /media/main.css with /media/main.css?20012931203128. I assumed that it will force browsers to reload css file when timestamp is changed (file is updated) and use local cached version in other case. But what I see in Apache logs (and at firebug) is that browsers (at least Firefox) requests CSS/JS files for each reload of the page, even after getting 304 code, see fragment from logs:

XXX.255.115.60 - - [24/Jul/2011:04:17:25 -0700] "GET /media/main.css?333900240611 HTTP/1.1" 304 172 "" "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
XXX.255.115.60 - - [24/Jul/2011:04:17:26 -0700] "GET /media/main.js?270101180511 HTTP/1.1" 304 173 "" "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
XXX.255.115.60 - - [24/Jul/2011:04:17:34 -0700] "GET /media/main.css?333900240611 HTTP/1.1" 304 172 "" "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
XXX.255.115.60 - - [24/Jul/2011:04:17:35 -0700] "GET /media/main.js?270101180511 HTTP/1.1" 304 173 "" "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
XXX.255.115.60 - - [24/Jul/2011:04:17:44 -0700] "GET /media/main.css?333900240611 HTTP/1.1" 304 172 "" "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
XXX.255.115.60 - - [24/Jul/2011:04:17:44 -0700] "GET /media/main.js?270101180511 HTTP/1.1" 304 173 "" "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"

Of course, it makes my website slower. Is it possible to force browsers to update files only when timestamp after .css?... is changed? Thanks!

Upd: Here is an example of response & request:

Request

User-Agent  Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18
Accept  text/css,*/*;q=0.1
Accept-Language en-gb,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset  ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive  115
Referer 
DNT 1
Connection  keep-alive
If-Modified-Since   Fri, 24 Jun 2011 05:39:33 GMT
If-None-Match   "8ed02f1-a21-4a66ea04f2f40"

Response

Date    Sun, 24 Jul 2011 12:28:21 GMT
Server  Apache
Connection  Keep-Alive
Keep-Alive  timeout=2, max=99
Etag    "8ed02f1-a21-4a66ea04f2f40"

Initial resp

Date    Sun, 24 Jul 2011 12:51:05 GMT
Server  Apache
Last-Modified   Fri, 24 Jun 2011 05:39:33 GMT
Etag    "8ed02f1-a21-4a66ea04f2f40"
Accept-Ranges   bytes
Content-Length  2593
Keep-Alive  timeout=2, max开发者_JAVA百科=99
Connection  Keep-Alive
Content-Type    text/css


You should use proper HTTP caching directives to make the responses never expire (one year in future):

Cache-Control: max-age=31536000
Expires: Sun, 24 Jul 2012 12:51:05 GMT

You can do this in Apache with mod_expires:

ExpiresByType text/css "now plus 1 year"
ExpiresByType text/javascript "now plus 1 year"



I played with this a lot and I found that it is probably caused by an Apache bug. This is not problem of the browser - once the timesatmp is in a query string, the browser doesn't understand the query string and must at least ask the server if it hasn't changed. The Apache responds well with '304 Not modified', but it always sends the file too!

I made a lot of experiment and the only solution that worked was to place the timestamp directly to file name. In PHP, I use the following function:

function safe_inline_url($file)
{

     $basename = basename($file);
     if (strstr($basename, '.'))
          return dirname($file) . "/" .
               preg_replace('/(\.[^\.]*)$/', '_ts_' . filemtime($file) . '\1',
                    $basename);
     else 
          return $file . '_ts_' . filemtime($file);

}

which modifies any inline URL (css, js, images, ...) so that it adds timestamp, eg. like js/forms.js => js/forms_ts_1278080148.js .

At the server, you must then rewrite the modified filenames back to the real names, which is accomplished by putting this to your .htaccess file:

RewriteEngine On

RewriteCond %{REQUEST_URI}      ^(.*)_ts_[0-9]{9,}$
RewriteRule ^                   %1      [PT]

RewriteCond %{REQUEST_URI}      ^(.*)_ts_[0-9]{9,}\.(.*)$
RewriteRule ^                   %1.%2   [PT]

you put it to root directory and you also must put it to every subdirectory's .htaccess with RewriteEngine On.

The only issue was with scriptaculous javascript library which uses URLs to include other sources - you cannot use this trick to include their js files.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜