diff --git a/modules/httpcache/httpcache.go b/modules/httpcache/httpcache.go index 3f23938be2f..27807697c61 100644 --- a/modules/httpcache/httpcache.go +++ b/modules/httpcache/httpcache.go @@ -99,7 +99,9 @@ func handleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag s return true } } - if lastModified != nil && !lastModified.IsZero() { + // https://www.rfc-editor.org/rfc/rfc9110#section-13.1.3 + // A recipient MUST ignore If-Modified-Since if the request contains an If-None-Match header field + if lastModified != nil && !lastModified.IsZero() && req.Header.Get("If-None-Match") == "" { ifModifiedSince := req.Header.Get("If-Modified-Since") if ifModifiedSince != "" { t, err := time.Parse(http.TimeFormat, ifModifiedSince) diff --git a/modules/httpcache/httpcache_test.go b/modules/httpcache/httpcache_test.go index 353d9ee62da..2362c20553f 100644 --- a/modules/httpcache/httpcache_test.go +++ b/modules/httpcache/httpcache_test.go @@ -76,6 +76,19 @@ func TestHandleGenericETagCache(t *testing.T) { wantHeaders: map[string]string{"Last-Modified": lastModified, "Cache-Control": "", "Etag": weakEtag}, wantStatus: http.StatusNotModified, }, + { + name: "If-Modified-Since without If-None-Match", + reqHeaders: map[string]string{"If-Modified-Since": lastModified}, + wantHandled: true, + wantHeaders: map[string]string{"Last-Modified": lastModified, "Cache-Control": "", "Etag": matchedEtag}, + wantStatus: http.StatusNotModified, + }, + { + name: "Mismatched If-None-Match takes precedence over If-Modified-Since", + reqHeaders: map[string]string{"If-None-Match": `"mismatched-etag"`, "If-Modified-Since": lastModified}, + wantHandled: false, + wantHeaders: map[string]string{"Last-Modified": lastModified, "Cache-Control": cacheControl, "Etag": matchedEtag}, + }, { name: "Multiple Matched If-None-Match", reqHeaders: map[string]string{"If-None-Match": `"mismatched-etag", ` + matchedEtag},