Bläddra i källkod

perf(ui): add max-age to Cache-Control

While inspecting the network requests done by miniflux' web interface, I
noticed that immutable assets with the `immutable` header were still fetched.
So I reread
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control:

> immutable tells a cache that the response is immutable while it's fresh and
avoids those kinds of unnecessary conditional requests to the server.

So it needs to have max-age as well, otherwise the browser will consider the
resource as not fresh, and will perform a network request (and get a 304).
jvoisin 1 vecka sedan
förälder
incheckning
533ff80744
2 ändrade filer med 6 tillägg och 3 borttagningar
  1. 4 1
      internal/http/response/builder.go
  2. 2 2
      internal/http/response/builder_test.go

+ 4 - 1
internal/http/response/builder.go

@@ -6,6 +6,7 @@ package response // import "miniflux.app/v2/internal/http/response"
 import (
 	"compress/flate"
 	"compress/gzip"
+	"fmt"
 	"io"
 	"log/slog"
 	"maps"
@@ -86,7 +87,9 @@ func (b *Builder) WithoutCompression() *Builder {
 func (b *Builder) WithCaching(etag string, duration time.Duration, callback func(*Builder)) {
 	etag = normalizeETag(etag)
 	b.headers.Set("ETag", etag)
-	b.headers.Set("Cache-Control", "public, immutable")
+	// max-age is required for the "immutable" directive to take effect: without
+	// it, browsers still revalidate content-hashed assets on every reload.
+	b.headers.Set("Cache-Control", fmt.Sprintf("public, max-age=%d, immutable", int64(duration.Seconds())))
 	b.headers.Set("Expires", time.Now().Add(duration).UTC().Format(http.TimeFormat))
 
 	if ifNoneMatch(b.r.Header.Get("If-None-Match"), etag) {

+ 2 - 2
internal/http/response/builder_test.go

@@ -240,7 +240,7 @@ func TestBuildResponseWithCachingEnabled(t *testing.T) {
 		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
 	}
 
-	expectedHeader := "public, immutable"
+	expectedHeader := "public, max-age=60, immutable"
 	actualHeader := resp.Header.Get("Cache-Control")
 	if actualHeader != expectedHeader {
 		t.Fatalf(`Unexpected cache control header, got %q instead of %q`, actualHeader, expectedHeader)
@@ -297,7 +297,7 @@ func TestBuildResponseWithCachingAndIfNoneMatch(t *testing.T) {
 				t.Fatalf(`Unexpected body, got %q instead of %q`, actual, tt.expectedBody)
 			}
 
-			if resp.Header.Get("Cache-Control") != "public, immutable" {
+			if resp.Header.Get("Cache-Control") != "public, max-age=60, immutable" {
 				t.Fatalf(`Unexpected Cache-Control header: %q`, resp.Header.Get("Cache-Control"))
 			}