I have these directives in my htaccess:
# BEGIN Compress text files <ifModule mod_deflate.c> <filesMatch "\.(css|js|x?html?|php)$"> SetOutputFilter DEFLATE </filesMatch> </ifModule> # END Compress text files # BEGIN Expire headers <ifModule mod_expires.c> ExpiresActive On ExpiresDefault "access plus 1 seconds" ExpiresByType image/x-icon "access plus 86400 seconds" ExpiresByType image/jpeg "access plus 86400 seconds" ExpiresByType image/png "access plus 86400 seconds" ExpiresByType image/gif "access plus 86400 seconds" ExpiresByType application/x-shockwave-flash "access plus 86400 seconds" ExpiresByType text/css "access plus 86400 seconds" ExpiresByType text/javascript "access plus 86400 seconds" ExpiresByType application/javascript "access plus 86400 seconds" ExpiresByType application/x-javascript "access plus 86400 seconds" ExpiresByType text/html "access plus 600 seconds" ExpiresByType application/xhtml+xml "access plus 600 seconds" </ifModule> # END Expire headers # BEGIN Cache-Control Headers <ifModule mod_headers.c> <filesMatch "\.(ico|jpe?g|png|gif|swf)$"> Header set Cache-Control "max-age=86400, public" </filesMatch> <filesMatch "\.(css)$"> Header set Cache-Control "max-age=86400, public" </filesMatch> <filesMatch "\.(js)$"> Header set Cache-Control "max-age=86400, private" </filesMatch> <filesMatch "\.(x?html?|php)$"> Header set Cache-Control "max-age=600, private, must-revalidate" </filesMatch> </ifModule> # END Cache-Control Headers # BEGIN Turn ETags Off <ifModule mod_headers.c> Header unset ETag </ifModule> FileETag None # END Turn ETags Off # BEGIN Remove Last-Modified Header <ifModule mod_headers.c> Header unset Last-Modified </ifModule> # END Remove Last-Modified Header
(taken from here )
I use Chrome for testing, and I noticed that the CSS files that should be cached are not actually cached:

Request header for one random CSS file:
Accept:text/css,*/*;q=0.1 Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3 Accept-Encoding:gzip,deflate,sdch Accept-Language:es-ES,es;q=0.8 Cache-Control:max-age=0 Connection:keep-alive Cookie:sua_language=en; keep=0; user=0; PHPSESSID=hn5gt5nb1j4sfq1j6m40un3it6; language=es Host:podo.com If-Modified-Since:Tue, 01 Nov 2011 17:20:59 GMT Referer:http://podo.com/sheet?id=48 User-Agent:Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.53 Safari/534.30
and the answer is:
Cache-Control:max-age=86400, public Connection:Keep-Alive Date:Tue, 28 Feb 2012 17:50:04 GMT Expires:Wed, 29 Feb 2012 17:50:04 GMT Keep-Alive:timeout=5, max=99 Server:Apache/2.2.15 (Unix) DAV/2 PHP/5.2.10 Vary:Accept-Encoding
I also have the following code snippet that runs before any JS file:
// Set document header header('Content-Type: text/javascript; charset=UTF-8'); $expires = ONE_DAY; header("Pragma: public"); header("Cache-Control: maxage=".$expires); header('Expires: ' . gmdate('D, d MYH:i:s', time()+$expires) . ' GMT');
and I noticed that if I comment on these lines and reload the page (without pressing F5 using the navigation buttons), some (not all) Javascript files will have the same behavior as CSS files (they need to be reloaded).
What's happening? Why are CSS files never cached? And why are JS files sometimes cached, sometimes not?
UPDATE I found that if I comment out the line <ifModule mod_expires.c> and the corresponding closing tag, everything works as expected, CSS files are not requested (as is the case with JS files). The question is why?
UPDATE 2 : response header on first receipt of CSS files (200 response):
Accept-Ranges:bytes Cache-Control:max-age=86400, public Connection:Keep-Alive Content-Encoding:gzip Content-Length:1633 Content-Type:text/css Date:Thu, 01 Mar 2012 16:01:53 GMT Expires:Fri, 02 Mar 2012 16:01:53 GMT Keep-Alive:timeout=5, max=99 Server:Apache/2.2.15 (Unix) DAV/2 PHP/5.2.10 Vary:Accept-Encoding
The request header is identical.