A response carrying "Cache-Control: max-age=00", "max-age=\"0\"" or
"s-maxage=00, max-age=60" is delivered from the cache for the whole
max-age of the cache (60 seconds by default) or for the max-age, while
its null lifetime means it must not be reused without revalidation.
Clients thus receive outdated contents. Such forms are unusual but valid
(RFC 9111#1.2.2 and #5.2 allow leading zeroes and quoted values).
http_check_response_for_cacheability() only recognizes the exact forms
"max-age=0" and "s-maxage=0" as null lifetimes. Other forms are taken as
freshness information, and then http_calc_maxage() only considers values
strictly greater than zero, so a null value is ignored and the next
source of lifetime is used: max-age after s-maxage, Expires, or the
cache's max-age.
http_calc_maxage() now uses s-maxage and max-age as soon as they were
found, including when they are null, and the store action does not store
a response whose remaining lifetime is null, since it would already be
expired. This last check also avoids storing responses with an Expires
date in the past, which were stored expired.
This patch may be backported to all supported versions. It depends on
the previous fixes to http_calc_maxage() ("BUG/MINOR: cache: reduce the
max-age lifetime by the response's Age" and "BUG/MINOR: cache: compare
the Age to the heuristic lifetime when none is given"), but only for
context, the change is easy to adapt.
if (age_relative)
- *age_relative = (smaxage > 0 || maxage > 0 || expires < 0);
+ *age_relative = (smaxage >= 0 || maxage >= 0 || expires < 0);
- if (smaxage > 0) {
+ /* note: a null value is a valid lifetime, it must not be ignored */
+ if (smaxage >= 0) {
if (true_maxage)
*true_maxage = smaxage;
return MIN(smaxage, cache->maxage);
}
- if (maxage > 0) {
+ if (maxage >= 0) {
if (true_maxage)
*true_maxage = maxage;
return MIN(maxage, cache->maxage);
goto out;
}
+ /* Do not store a response which would already be expired */
+ if (effective_maxage <= 0)
+ goto out;
+
/* Build a last-modified time that will be stored in the cache_entry and
* compared to a future If-Modified-Since client header. */
object->last_modified = get_last_modified_time(htx);