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’m using Chrome to test and I’ve noticed that the CSS files that should be cached, really are not cached:

The request header for one random CSS file is:
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 response 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 piece of code that is executed 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 M Y H:i:s', time()+$expires) . ' GMT');
and I have noticed that if I comment these lines and reload the page (not by pressing F5, using navigation buttons), some (not all) Javascript files has the same behaviour than the CSS files (are forced to reload).
What’s happening? Why the CSS files are never cached? And why the JS files are sometimes cached, sometimes not?
UPDATE: I’ve discovered that if I comment the line <ifModule mod_expires.c> and it corresponding closing tag, everything works as expected, it is, CSS files are not requested (as happens with JS files). The question now is: why?
UPDATE 2: Response header when a CSS files is received first time (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
Request header is identical.
In fact they are cached. The important thing to note here is this line of the request:
Chrome is asking for the server to send a new copy of the file if and only if it has been modified since
Tue, 01 Nov 2011 17:20:59 GMT.The expected response here would be one of two things: If the file has been modified, the server should return the new version in the body of the response with a
200 OKresponse code. If the file has not been modified, the server should give a304 Not Modifiedresponse and the body of the response will be empty – if you look at the response code showed in the console, you will see that this is what is happening.For more information, see this section and this section of RFC2616.
Footnote: what is interesting, I have not previously observed and I cannot readily explain is why
privateresources are used with rechecking the server, andpublicresources are not. At least, this is what I assume is happening based on the fact that your JS files are used straight from the cache and your CSS files are not.