I’ve setup a browser cache for a static site via the .htaccess
file by setting:
# BROWER CACHING - 1 Day for images
<filesMatch ".(jpg|jpeg|gif|ico)$">
Header set Cache-Control "max-age=86400, public"
</filesMatch>
I’m fine with these images having a 1 day cache, but the site changes frequently and thus I don’t want to cache the CSS and JS files.
I’ve read about ETag, which, as I understand, allows you to cache a file, but also set its creation date, so if it gets updated the next time a client visits the site, it will check if the creation date matches.
You would use either FileETag MTime Size
or Header unset Etag
and FileEtag none
. Do not use both (Create ETag and Remove ETag) and only choose which one works best on your particular server.
# Create the ETag (entity tag) response header field
FileETag MTime Size
or
# Remove the ETag (entity tag) response header field
Header unset ETag
FileETag none
HTTP has several features related to caching and they apply to both user agent (browser) cache and proxy caches (whether transparent, or not; e.g. proxy in the client’s network or a reverse proxy sitting just next to the server). These features come in two groups: expiration (may prevent request entirely) and validation (may prevent transfer of data).
Entity tag (ETag
) is just one of these features and belongs to the validation group. The other one in this group is last modification time (Last-Modified
). Entity tag allows for cache invalidation due to contents change instead of newer last modification time. Read more on how entity tag works on Wikipedia. In short, the typical usage is:
The server adds ETag
header to a response containing a resource being served.
The client caches the resource and remembers its entity tag (the value of ETag
).
Next time the client needs the resource, it requests it from the server conditionally. In the request, it includes If-None-Match
header containing the entity tag.
If the resource changed (the entity tag in If-None-Match
is considered to be stale by the server), the server sends a response containing the current version of the resource (and the new entity tag), otherwise it just responds with 304 Not Modified
and does not bother to send the resource again.
For static files (not created dynamically by a CGI script or so, on each request), Apache may be configured to generate ETag
via FileETag
directive. By default, without you making any changes to the configuration, Apache will generate ETag
and its value will be based on the file’s last modification time (mtime) and size in Apache 2.4. In Apache 2.3.14, the default used to include the file’s inode number, too.
If the file is served dynamically, Apache cannot generate the ETag
, because it does not know the details of how the resource to be cached is generated. It is up to the script to set ETag
appropriately and to handle the If-None-Match
. E.g. in mod_perl, the If-None-Match
part can be handled using Apache2::Request::meets_conditions, which implements handling of HTTP/1.1 conditional requests in general.
If you want to rely solely on ETag, you have to disable other validation features and the expiration mechanism. Set Cache-Control: max-age=0, must-revalidate
and Expires: 0
to force the revalidation of cache entries (i.e. always make a request). You may also remove the Last-Modified
header from the responses, but HTTP/1.1 advises against that, in general.
For comparison of Last-Modified
and ETag
, see these:
Note that Last-Modified
is seen as a HTTP/1.0 compatibility feature. ETag
may contain the same value and work exactly the same (except using If-None-Match
instead of If-Modified-Since
).
As a side note, I’d like to add that proposed standard RFC 7232 exists and it is related to details of entity tags and conditional requests. See its appendix A for changes it introduces from HTTP/1.1.
ETAG is not the most important attribute. The main attribute you're missing is expires. I'm 100% percent sure, browser cache will work without etag. Check the below configuration on http://pisrs.si. How to check? Hit F12 in browser, goto network tab, see how resources are fetched, compare to your site. Localhost resources are cached in different way. Check your browser info on that.
Below is working configuration from main domain, that is working. Make sure you have the necessary mods enabled.
<IfModule mod_mime.c>
AddType text/css .css
AddType application/x-javascript .js
AddType image/bmp .bmp
AddType image/gif .gif
AddType application/x-gzip .gz .gzip
AddType image/x-icon .ico
AddType image/jpeg .jpg .jpeg .jpe
AddType image/png .png
AddType application/x-font-ttf .ttf .ttc
AddType application/zip .zip
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css A31536000
ExpiresByType application/x-javascript A31536000
ExpiresByType application/javascript A31536000
ExpiresByType text/javascript A31536000
ExpiresByType text/x-js A31536000
ExpiresByType image/bmp A31536000
ExpiresByType image/gif A31536000
ExpiresByType application/x-gzip A31536000
ExpiresByType image/x-icon A31536000
ExpiresByType image/jpeg A31536000
ExpiresByType application/x-font-otf A31536000
ExpiresByType image/png A31536000
ExpiresByType application/x-font-ttf A31536000
ExpiresByType application/zip A31536000
</IfModule>
<IfModule mod_deflate.c>
<IfModule mod_headers.c>
Header append Vary User-Agent env=!dont-vary
</IfModule>
AddOutputFilterByType DEFLATE text/html text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/plain image/x-icon image/png image/gif
<IfModule mod_mime.c>
# DEFLATE by extension
AddOutputFilter DEFLATE js css htm html xml png gif
</IfModule>
</IfModule>
<FilesMatch "\.(gif|ico|jpg|jpeg|png|GIF|ICO|JPG|JPEG|PNG|css|js|woff|CSS|JS|WOFF|ttf|TTF)$">
<IfModule mod_headers.c>
Header unset Set-Cookie
Header set Cache-Control "max-age=31536000, public"
</IfModule>
</FilesMatch>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With