]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: cache: do not ignore null lifetimes not written as "max-age=0"
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 21 Oct 2026 09:02:42 +0000 (09:02 +0000)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 12 Nov 2026 17:20:32 +0000 (18:20 +0100)
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.

src/cache.c

index ff75bcfe10f325e6db79e9194e6da296b1eacf2a..21d34c001e6e083dd4d7da89be8f58b256708fa6 100644 (file)
@@ -1121,15 +1121,16 @@ int http_calc_maxage(struct stream *s, struct cache *cache, int *true_maxage, in
 
 
        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);
@@ -1846,6 +1847,10 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
                        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);