From 3e46ba5ce2e382e7b388103427c20c21c11fc839 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 29 Sep 2025 13:05:24 +0200 Subject: [PATCH] cache/httpcache: Add respectCacheControlNoStoreInResponse and respectCacheControlNoStoreInRequest options This cache is used by `resources.GetRemote`. Default values are: * respectCacheControlNoStoreInResponse: false * respectCacheControlNoStoreInRequest: true This is a slightly breaking change, but the current behaviour is confusing, as: * Many servers set the `no-store` header without much consideration, see https://developer.chrome.com/docs/web-platform/bfcache-ccns for more context * We almost always want to cache the `resources.GetRemote` to disk. Fixes #13990 --- cache/httpcache/httpcache.go | 15 +++++++++++++-- go.mod | 2 +- go.sum | 2 ++ resources/resource_factories/create/create.go | 13 +++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/cache/httpcache/httpcache.go b/cache/httpcache/httpcache.go index bd6d4bf7d..010b12e57 100644 --- a/cache/httpcache/httpcache.go +++ b/cache/httpcache/httpcache.go @@ -25,6 +25,8 @@ import ( // DefaultConfig holds the default configuration for the HTTP cache. var DefaultConfig = Config{ + RespectCacheControlNoStoreInRequest: true, + RespectCacheControlNoStoreInResponse: false, Cache: Cache{ For: GlobMatcher{ Excludes: []string{"**"}, @@ -42,7 +44,13 @@ var DefaultConfig = Config{ // Config holds the configuration for the HTTP cache. type Config struct { - // Configures the HTTP cache behavior (RFC 9111). + // When enabled and there's a Cache-Control: no-store directive in the request, response will never be stored in disk cache. + RespectCacheControlNoStoreInRequest bool + + // When enabled and there's a Cache-Control: no-store directive in the response, response will never be stored in disk cache. + RespectCacheControlNoStoreInResponse bool + + // Enables HTTP cache behavior (RFC 9111) for these resources. // When this is not enabled for a resource, Hugo will go straight to the file cache. Cache Cache @@ -57,7 +65,9 @@ type Cache struct { } func (c *Config) Compile() (ConfigCompiled, error) { - var cc ConfigCompiled + cc := ConfigCompiled{ + Base: *c, + } p, err := c.Cache.For.CompilePredicate() if err != nil { @@ -127,6 +137,7 @@ func (gm GlobMatcher) IsZero() bool { } type ConfigCompiled struct { + Base Config For predicate.P[string] PollConfigs []PollConfigCompiled } diff --git a/go.mod b/go.mod index da209774a..9fabd69f6 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/gobwas/glob v0.2.3 github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e github.com/gohugoio/hashstructure v0.5.0 - github.com/gohugoio/httpcache v0.7.0 + github.com/gohugoio/httpcache v0.8.0 github.com/gohugoio/hugo-goldmark-extensions/extras v0.5.0 github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1 github.com/gohugoio/locales v0.14.0 diff --git a/go.sum b/go.sum index 16ef9c7c4..e93720d8e 100644 --- a/go.sum +++ b/go.sum @@ -268,6 +268,8 @@ github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp4 github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs= github.com/gohugoio/httpcache v0.7.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI= +github.com/gohugoio/httpcache v0.8.0 h1:hNdsmGSELztetYCsPVgjA960zSa4dfEqqF/SficorCU= +github.com/gohugoio/httpcache v0.8.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI= github.com/gohugoio/hugo-goldmark-extensions/extras v0.5.0 h1:dco+7YiOryRoPOMXwwaf+kktZSCtlFtreNdiJbETvYE= github.com/gohugoio/hugo-goldmark-extensions/extras v0.5.0/go.mod h1:CRrxQTKeM3imw+UoS4EHKyrqB7Zp6sAJiqHit+aMGTE= github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1 h1:nUzXfRTszLliZuN0JTKeunXTRaiFX6ksaWP0puLLYAY= diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go index 14b17a105..e8a0b4c4d 100644 --- a/resources/resource_factories/create/create.go +++ b/resources/resource_factories/create/create.go @@ -111,6 +111,19 @@ func New(rs *resources.Spec) *Client { ShouldCache: func(req *http.Request, resp *http.Response, key string) bool { return shouldCache(resp.StatusCode) }, + CanStore: func(reqCacheControl, respCacheControl httpcache.CacheControl) (canStore bool) { + if httpCacheConfig.Base.RespectCacheControlNoStoreInResponse { + if _, ok := respCacheControl["no-store"]; ok { + return false + } + } + if httpCacheConfig.Base.RespectCacheControlNoStoreInRequest { + if _, ok := reqCacheControl["no-store"]; ok { + return false + } + } + return true + }, MarkCachedResponses: true, EnableETagPair: true, Transport: &transport{ -- 2.39.5