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"

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

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.

  • 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).



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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.