diff --git a/apps/web/src/middleware/global-security.ts b/apps/web/src/middleware/global-security.ts index 7a5386ab2..8584fd7f0 100644 --- a/apps/web/src/middleware/global-security.ts +++ b/apps/web/src/middleware/global-security.ts @@ -11,6 +11,31 @@ export const securityMiddleware = createMiddleware({ type: 'function' }).server( setHeaders(createSecurityHeaders(isEmbed)); + // Set appropriate cache headers for static assets + const pathname = url.pathname; + if ( + pathname.endsWith('.ico') || + pathname.endsWith('.png') || + pathname.endsWith('.jpg') || + pathname.endsWith('.jpeg') || + pathname.endsWith('.gif') || + pathname.endsWith('.svg') || + pathname.endsWith('.woff') || + pathname.endsWith('.woff2') || + pathname.endsWith('.ttf') || + pathname.endsWith('.eot') + ) { + // Static assets with hashed filenames can be cached for 1 year + setHeaders({ + 'Cache-Control': 'public, max-age=31536000, immutable', // 1 year + }); + } else if (pathname === '/manifest.json') { + // Manifest can be cached for a shorter time + setHeaders({ + 'Cache-Control': 'public, max-age=86400', // 1 day + }); + } + const result = await next(); return result; } diff --git a/apps/web/src/middleware/shared-headers.ts b/apps/web/src/middleware/shared-headers.ts index 574a1ab95..6d1325ddc 100644 --- a/apps/web/src/middleware/shared-headers.ts +++ b/apps/web/src/middleware/shared-headers.ts @@ -1,3 +1,10 @@ +// Reasonable cache headers for HTML pages +export const htmlCacheHeaders = [ + { httpEquiv: 'Cache-Control', content: 'public, max-age=300, must-revalidate' }, // 5 minutes + { httpEquiv: 'Pragma', content: 'cache' }, +]; + +// Only use aggressive no-cache for specific routes that need it export const preventBrowserCacheHeaders = [ { httpEquiv: 'Cache-Control', content: 'no-cache, no-store, must-revalidate' }, { httpEquiv: 'Pragma', content: 'no-cache' }, diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index d0cc9dbc8..56617c206 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -1,6 +1,6 @@ import { createRootRouteWithContext, HeadContent, Scripts } from '@tanstack/react-router'; import { RootProviders } from '@/context/Providers'; -import { preventBrowserCacheHeaders } from '@/middleware/shared-headers'; +import { htmlCacheHeaders } from '@/middleware/shared-headers'; import shareImage from '../assets/png/default_preview.png'; import favicon from '../assets/png/favicon.ico'; import { TanstackDevtools } from '../integrations/tanstack-dev-tools/tanstack-devtools'; @@ -31,7 +31,7 @@ export const Route = createRootRouteWithContext()({ { name: 'og:type', content: 'website' }, { name: 'og:locale', content: 'en_US' }, { name: 'og:site_name', content: 'Buster' }, - ...preventBrowserCacheHeaders, + ...htmlCacheHeaders, ], links: [ { rel: 'stylesheet', href: appCss },