user4039871
user4039871

Reputation:

Enable caching of css and js files in Apache

Using Apache 2.4 on Debian 8.2, I am trying to enable caching of all css and js files. Caching of images works fine; that is, the browser receives a 304 status, so it doesn't download again. But I cannot get caching of other files working.

I use this inside a virtual host file:

<IfModule mod_expires.c>
    <FilesMatch "\.(jpe?g|png|gif|js|css)$">
        ExpiresActive On
        ExpiresDefault "access plus 1 week"
    </FilesMatch>
</IfModule>

The expires module is enabled. I did restart apache, cleaned browser cookies, etc. No success.

The response for a gif image, from browser developer tools:

Cache-Control:max-age=604800
Connection:Keep-Alive
Date:Wed, 25 Nov 2015 21:37:50 GMT
ETag:"4174a-4e69c97fbf080"
Expires:Wed, 02 Dec 2015 21:37:50 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.10 (Debian)

The response for a css file:

Accept-Ranges:bytes
Cache-Control:max-age=604800
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:135
Content-Type:text/css
Date:Wed, 25 Nov 2015 21:37:50 GMT
ETag:"5116-525639d271c78-gzip"
Expires:Wed, 02 Dec 2015 21:37:50 GMT
Keep-Alive:timeout=5, max=99
Last-Modified:Wed, 25 Nov 2015 20:50:52 GMT
Server:Apache/2.4.10 (Debian)
Vary:Accept-Encoding

It looks like the expires heading is set correctly, but the browser keeps requesting the file (200 OK).

I tried with Chrome and Firefox.

Summary:

1.) When I follow links inside the web site, the browser uses the cached files. But when I press F5, they re-download the css and js files, but they don't re-download images. Images give 304. That is fine.

2.) When I press Ctrl-F5, naturally enough, all files are re-downloaded. That's fine too.

3.) So the problem is how to enable caching (just like images) for other files. Why is apache discriminating between images and other files? I didn't put anything special to images in the config files.

Q: How to properly enable caching of css and js files?

Another Q: is there a special http header that says to the browser never to request the file. The reason is, sending even a request to check if the file is modified takes 100-200 ms, which is too much. I am sure the files will not be modified. And if they are modified, I can easily put a version string at the end of the css file, such as myFile.css?v=1.1 So I hope there should be a way to stop sending requests completely.

SOLVED

First, there is a bug in apache as mentioned in the answer below.

Second, there was a misunderstanding on my part. I guess this is how modern browsers work:

1.) Follow links inside a web site: No request is sent, even to check if the file has been modified.

2.) F5: Send a request. If file is not modified then the server responds 304.

3.) Ctrl+F5: Full download.

The behavior about F5 does not make sense to me. Anyway.

In case anybody needs it, here is a working solution that I put into virtual host file:

RequestHeader  edit "If-None-Match" "^\"(.*)-gzip\"$" "\"$1\""
Header  edit "ETag" "^\"(.*[^g][^z][^i][^p])\"$" "\"$1-gzip\""

LoadModule expires_module /usr/lib/apache2/modules/mod_expires.so
ExpiresActive On

<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
    ExpiresDefault "access plus 4 weeks"
</FilesMatch>

Upvotes: 14

Views: 24951

Answers (2)

Samuel Aiala Ferreira
Samuel Aiala Ferreira

Reputation: 694

If nothing else seems to work, don't forget to turn the disable cache from devtools in you browser!!!

enter image description here

Upvotes: 1

Barry Pollard
Barry Pollard

Reputation: 46070

Turn off Etags, they don't play well in Apache when gzip is on for 304s.

See here: Apache is not sending 304 response (if mod_deflate and AddOutputFilterByType is enabled)

As images are already compressed they are typically not gzipped and hence why they work.

ETags are not that useful in my opinion in their current implementation (see my blog here for a more in depth discussion as to why) so, coupled with above bug, I turn them off.

For your second question just set a long expiry.

As discussed in the comments below there are three scenarios:

  1. Normal browsing - in which caching should be used and 304s only used if cache is still valid after expiry (in which case it's set to valid again for same expiry).

  2. F5 or Refresh button. This is an explicit action by the user to confirm the page and all its resources are still valid so they all will be double checked (even those still in cache and still valid according to expiries header) and 304s sent when they haven't changed. It does not mean "just redownload anything which has expired but leave cached items alone as they are still valid" as you think it should. Personally I think the current implementation the browsers use makes sense and your method would be confusing to end users. While some sites may version assets like images, css and JavaScript so rechecking is a waste of time, not all such sites do this.

  3. Ctrl+F5. This is a force refresh. It means "Ignore cache and download everything". It's rarely needed except by developers who change files requested on development servers.

Hope that all makes sense.

Edit 12 May 2016: Looks like Firefox is bringing in the functionality you actually want: https://bitsup.blogspot.ie/2016/05/cache-control-immutable.html?m=1

Upvotes: 3

Related Questions