feat(static): add browser cache-control for static files#111
Merged
Conversation
- Add Cache-Control headers to static file and favicon responses - Content-hashed files in /static/dist/ get immutable caching (1 year) - Other static files use configurable STATIC_CACHE_MAX_AGE (default: 24h) - Setting STATIC_CACHE_MAX_AGE=0 disables caching for non-hashed files - Add tests for cache header behavior across all configurations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds configurable Cache-Control behavior for static assets served by the Gin router, aiming to reduce browser re-fetches while keeping content-hashed bundles highly cacheable.
Changes:
- Introduces
StaticCacheMaxAgeconfig loaded fromSTATIC_CACHE_MAX_AGE(default24h,0disables for non-hashed assets). - Replaces
gin.StaticFSwith a custom static handler that applies immutable caching for/static/dist/*and configurable caching elsewhere. - Adds tests + embedded testdata fixtures to validate cache header behavior for dist/non-dist and zero/custom max-age cases.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/config/config.go | Adds StaticCacheMaxAge to config and loads it from STATIC_CACHE_MAX_AGE. |
| internal/bootstrap/router.go | Implements Cache-Control header logic for /static/* and /favicon.ico. |
| internal/bootstrap/static_cache_test.go | Adds tests around static/favicons cache headers using embedded testdata. |
| internal/bootstrap/testdata/internal/templates/static/images/favicon.ico | Adds favicon fixture for tests. |
| internal/bootstrap/testdata/internal/templates/static/dist/main-62KIAYER.css | Adds dist asset fixture for tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
internal/bootstrap/router.go
Outdated
Comment on lines
+106
to
+115
| fileServer := http.StripPrefix("/static", http.FileServer(http.FS(staticFS))) | ||
| r.GET("/static/*filepath", func(c *gin.Context) { | ||
| path := c.Param("filepath") | ||
| if strings.HasPrefix(path, "/dist/") { | ||
| c.Header("Cache-Control", "public, max-age=31536000, immutable") | ||
| } else if cacheMaxAge > 0 { | ||
| c.Header("Cache-Control", fmt.Sprintf("public, max-age=%d", int(cacheMaxAge.Seconds()))) | ||
| } | ||
| fileServer.ServeHTTP(c.Writer, c.Request) | ||
| }) |
internal/bootstrap/router.go
Outdated
Comment on lines
+108
to
+114
| path := c.Param("filepath") | ||
| if strings.HasPrefix(path, "/dist/") { | ||
| c.Header("Cache-Control", "public, max-age=31536000, immutable") | ||
| } else if cacheMaxAge > 0 { | ||
| c.Header("Cache-Control", fmt.Sprintf("public, max-age=%d", int(cacheMaxAge.Seconds()))) | ||
| } | ||
| fileServer.ServeHTTP(c.Writer, c.Request) |
Comment on lines
+92
to
+106
| func TestFaviconCacheControl(t *testing.T) { | ||
| gin.SetMode(gin.TestMode) | ||
| r := gin.New() | ||
| // createFaviconHandler reads from embed.FS at a fixed path; | ||
| // use the testdata FS which has the file at the adjusted path. | ||
| faviconData, err := testdataFS.ReadFile("testdata/internal/templates/static/images/favicon.ico") | ||
| require.NoError(t, err) | ||
|
|
||
| cacheMaxAge := 24 * time.Hour | ||
| r.GET("/favicon.ico", func(c *gin.Context) { | ||
| if cacheMaxAge > 0 { | ||
| c.Header("Cache-Control", fmt.Sprintf("public, max-age=%d", int(cacheMaxAge.Seconds()))) | ||
| } | ||
| c.Data(http.StatusOK, "image/x-icon", faviconData) | ||
| }) |
…iles - Only set Cache-Control on successful responses to prevent caching 404s - Register HEAD handler for static files to support CDN cache validation - Extract createFaviconHandlerFromBytes for direct test coverage - Add tests for 404 no-cache and HEAD request scenarios Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cache-Controlheaders to static file and favicon responses to reduce unnecessary re-fetches/static/dist/get immutable caching (max-age=31536000, immutable)STATIC_CACHE_MAX_AGEenv var (default:24h, set to0to disable)Test plan
make generate && make build— builds successfullymake test— all tests pass (21 bootstrap tests, full suite green)make lint— 0 issuesCache-Controlheaders on static assets🤖 Generated with Claude Code