diff --git a/backend/app/administration.go b/backend/app/administration.go index 3ded862f..8161f66d 100644 --- a/backend/app/administration.go +++ b/backend/app/administration.go @@ -319,8 +319,9 @@ func setupRoutes( // phishing server (app/server.go AssignRoutes), gated to a single global // domain, so the admin port does not need public exposure for SCIM. - // all other admin routes are protected by the ip allowlist middleware - admin := r.Group("/", middleware.IPLimiter) + // ip allowlist on all admin routes; NoStore keeps API responses out of the + // browser disk cache. static assets are served off the group and stay cacheable. + admin := r.Group("/", middleware.IPLimiter, middleware.NoStore) _ = admin if !build.Flags.Production { diff --git a/backend/app/middleware.go b/backend/app/middleware.go index 6b838868..e45b7213 100644 --- a/backend/app/middleware.go +++ b/backend/app/middleware.go @@ -15,6 +15,7 @@ type Middlewares struct { LoginRateLimiter gin.HandlerFunc SessionHandler gin.HandlerFunc SoftSessionHandler gin.HandlerFunc + NoStore gin.HandlerFunc } // NewMiddlewares creates a collection of middlewares @@ -48,6 +49,7 @@ func NewMiddlewares( LoginRateLimiter: loginThrottle, SessionHandler: sessionHandler, SoftSessionHandler: softSessionHandler, + NoStore: middleware.NoStore(), } } diff --git a/backend/middleware/noStore.go b/backend/middleware/noStore.go new file mode 100644 index 00000000..c0e2b86b --- /dev/null +++ b/backend/middleware/noStore.go @@ -0,0 +1,12 @@ +package middleware + +import "github.com/gin-gonic/gin" + +// NoStore keeps responses out of the browser disk cache. A handler may set +// its own Cache-Control afterwards to override it. +func NoStore() gin.HandlerFunc { + return func(c *gin.Context) { + c.Header("Cache-Control", "no-store") + c.Next() + } +}