commit 5563d32bc82884394ef7bfff647d21d632a6f178
parent ec9b947472ab9436481f43599a924b110e2ba5f5
Author: David Eisinger <[email protected]>
Date: Thu, 9 Oct 2025 14:32:36 -0400
Cache busting
Diffstat:
2 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/internal/api.go b/internal/api.go
@@ -362,6 +362,7 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) {
html = out
}
}
+ html = applyCacheBust(html)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(html)
return
@@ -441,6 +442,7 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) {
}
}
}
+ html = applyCacheBust(html)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(html)
return
diff --git a/internal/static.go b/internal/static.go
@@ -3,6 +3,8 @@ package internal
import (
"bytes"
"embed"
+ "crypto/sha256"
+ "fmt"
"io/fs"
"net/http"
"path"
@@ -46,6 +48,8 @@ func StaticHandlerWithConfig(cfg HandlerConfig) (http.Handler, error) {
indexBytes = nb
}
}
+ // Apply cache-busting query params to static asset URLs
+ indexBytes = applyCacheBust(indexBytes)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := strings.TrimPrefix(r.URL.Path, "/")
if p == "" {
@@ -94,3 +98,39 @@ func RenderPage(name string, cfg HandlerConfig) ([]byte, error) {
}
return pageBytes, nil
}
+
+// applyCacheBust appends a short content hash to known asset URLs in HTML,
+// e.g., /app.js?v=abcd1234. This helps CDNs like Cloudflare pick up changes
+// without manual purges.
+func applyCacheBust(html []byte) []byte {
+ sub, err := fs.Sub(webAssets, "web")
+ if err != nil { return html }
+ // Build fingerprint map for assets we serve locally
+ assets := []string{
+ "app.js",
+ "styles.css",
+ "vendor/vue.global.prod.js",
+ "vendor/marked.min.js",
+ "vendor/purify.min.js",
+ }
+ bust := map[string]string{}
+ for _, p := range assets {
+ if b, err := fs.ReadFile(sub, p); err == nil {
+ sum := sha256.Sum256(b)
+ bust[p] = fmt.Sprintf("%x", sum)[:10]
+ }
+ }
+ // Replace src/href occurrences
+ out := html
+ for p, v := range bust {
+ // src="/path"
+ from := []byte("src=\"/"+p+"\"")
+ to := []byte("src=\"/"+p+"?v="+v+"\"")
+ out = bytes.ReplaceAll(out, from, to)
+ // href="/path"
+ fromH := []byte("href=\"/"+p+"\"")
+ toH := []byte("href=\"/"+p+"?v="+v+"\"")
+ out = bytes.ReplaceAll(out, fromH, toH)
+ }
+ return out
+}