diff --git a/frontend/src/sw.ts b/frontend/src/sw.ts index edad75436..07c26685f 100644 --- a/frontend/src/sw.ts +++ b/frontend/src/sw.ts @@ -20,10 +20,14 @@ workbox.routing.registerRoute( new workbox.strategies.StaleWhileRevalidate(), ) -// Always send api requests through the network +// Always send api requests through the network and bypass the browser's HTTP cache workbox.routing.registerRoute( new RegExp('api\\/v1\\/.*$'), - new workbox.strategies.NetworkOnly(), + new workbox.strategies.NetworkOnly({ + fetchOptions: { + cache: 'no-store', + }, + }), ) // This code listens for the user's confirmation to update the app. diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index d078352e0..4b35ee9a3 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -283,6 +283,17 @@ func collectRoutesForAPITokens(e *echo.Echo) { func registerAPIRoutes(a *echo.Group) { + // Prevent browsers from caching API responses. Without an explicit + // Cache-Control header browsers may heuristically cache JSON responses + // which causes stale data (e.g. newly team-shared projects not appearing + // until a hard refresh). + a.Use(func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c *echo.Context) error { + c.Response().Header().Set("Cache-Control", "no-store") + return next(c) + } + }) + // This is the group with no auth // It is its own group to be able to rate limit this based on different heuristics n := a.Group("")