How ExpiresByType and Header set Cache-Control works together?

How ExpiresByType and Header set Cache-Control works together? - .htaccess files are extremely useful in many cases for users who either do not have root permissions or for users who simply aren't comfortable in making changes in their web server's configuration file. Trying to debug .htaccess not working isn't always the easiest thing to do, however, hopefully by checking the discuss below mentioned about htaccess, cache, cache-control, mod-headers, .htaccess common problems as well as the troubleshooting tips, you'll have a better grasp on what you may have to modify to get your .htaccess file running smoothly.Problem :


I have:


ExpiresActive On
ExpiresByType image/jpg "access 2 years"

And let's say:


<filesMatch ".(jpg|jpeg|png|gif)$">
Header set Cache-Control "max-age=31536000, public"
</filesMatch>

When will jpg files expire ? In 2 years or in one year (31536000 seconds) ?


Solution :


ExpiresByType image/jpg "access 2 years"


First... to clarify, this should probably be image/jpeg, not image/jpg. image/jpeg is the official mime-type for JPEG files. Check the Content-Type HTTP response header associated with this response. For instance, if your server is sending JPEG files with a image/jpeg mime-type (as it should be), then the above directive is not doing anything.


Note that image/jpg has nothing to do with the file extension (eg. .jpg) that might be on the underlying filename. It refers to the mime-type the server is sending the resource as.


So, for the remainder of my answer, I assume this is the correct mime-type that your server is sending.



When will jpg files expire ? In 2 years or in one year (31536000 seconds) ?



The answer to this does depend on the type of request/response and what you are doing server-side. ie. Does the request map directly to a physical file or not? Since you mention "jpg files" then #1 below probably applies here (the most common scenario) and the "jpg file" will expire in 1 year.


1. Request maps directly to a physical .jpg file


The request maps directly to a physical JPEG file on disk (either directly or via an internal rewrite*1). eg. /myimage.jpg - which would indeed be the most common use case (ie. you are linking directly to your static resources) then the JPEG file will expire in 1 year, by the Header directive. This is because:



  1. <FilesMatch> (and <Files>) containers only match physical files.

  2. The Header directive is processed later and so will always override the Cache-Control header that mod_expires might otherwise set.


*1 Note that you could link to (ie. request) /get-image.php, but providing the request is internally rewritten directly to an underlying .jpg file on disk, eg. /some-image.jpg then the same applies.


2. Request does NOT map directly to a physical file


HOWEVER, if the request does not map to a physical file. For example, if either of the following scenarios is true (both are really the same):



  • You are linking to a PHP script that serves the JPEG image (either by creating it, or reading it from a different location, etc.) and there is no internal rewrite to a physical file.


    href="send-image.php?name=myimage.jpg&size=medium"


  • You are linking to a .jpg URL, eg. href="/myotherimage-medium.jpg" and you are internally rewriting this to a script (like above) that generates this image and returns it to the client.




Then the JPEG image will expire in 2 years since the <FilesMatch> directive will not apply, because the request does not map directly to a physical file.


Note that in both cases (scenario #1 and #2 above), the ExpiresByType directive will apply, but the Header directive will always override this if it is applied.


Actually, there is a further complication here... mod_expires sets 2 response headers Cache-Control: max-age and Expires. Expires is only for old browsers, all modern browsers prioritise the Cache-Control: max-age header. So, in case #1 above, where the request maps directly to a physical file then you'll have a conflict of headers... Cache-Control: max-age will state that it expires in 1 year (overridden by the Header directive), whereas the Expires header will state that it expires in 2 years (set my mod_expires).


So, unless you have specific caching requirements*2 then use mod_expires only.


*2 mod_expires only sets the max-age directive on the Cache-Control header. If you need to set other directives, eg. no-store or must-revalidate etc. then you will need to use the Header directive instead (or as well as).


Reference:




Meanwhile I found a subject on stackoverflow: https://stackoverflow.com/questions/5799906/what-s-the-difference-between-expires-and-cache-control-headers


There, one user also gives a link to w3: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html which says:



If a response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header, even if the Expires header is more restrictive. This rule allows an origin server to provide, for a given response, a longer expiration time to an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache. This might be useful if certain HTTP/1.0 caches improperly calculate ages or expiration times, perhaps due to desynchronized clocks.



Additionally, if you would like to do some further testing, give the htaccess tester tool a try. It allows you to specify a certain URL as well as the rules you would like to include and then shows which rules were tested, which ones met the criteria, and which ones were executed.

Comments

Popular posts from this blog

Rewrite in Mediawiki, remove index.php, .htaccess

.htaccess rewrite wildcard folder paths from host

Using .htaccess to set a cookie and 301 redirect