Files
pulse/next.config.ts
Usman Baig 2f5bcf479a fix: add Cache-Control no-cache for HTML pages to prevent stale CDN content
Bunny CDN was caching HTML pages, so after deploys the browser kept
loading old JS bundles with expired Server Action hashes. This header
tells the CDN to always revalidate with the origin. Static assets
(/_next/static/*) are excluded since they are content-hashed.
2026-03-07 20:12:11 +01:00

102 lines
3.0 KiB
TypeScript

import type { NextConfig } from 'next'
import withPWAInit from "@ducanh2912/next-pwa"
const withPWA = withPWAInit({
dest: "public",
register: true,
disable: process.env.NODE_ENV === "development",
})
// * CSP directives — restrict resource loading to known origins
const cspDirectives = [
"default-src 'self'",
// Next.js requires 'unsafe-inline' for its bootstrap scripts; 'unsafe-eval' only in dev (HMR)
`script-src 'self' 'unsafe-inline'${process.env.NODE_ENV === 'development' ? " 'unsafe-eval'" : ''}`,
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob: https://www.google.com https://*.gstatic.com https://ciphera.net",
"font-src 'self'",
`connect-src 'self' https://*.ciphera.net https://ciphera.net https://www.google.com https://*.gstatic.com https://cdn.jsdelivr.net${process.env.NODE_ENV === 'development' ? ' http://localhost:*' : ''}`,
"worker-src 'self'",
"frame-src 'none'",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self' https://*.ciphera.net",
].join('; ')
const nextConfig: NextConfig = {
reactStrictMode: true,
// * Enable standalone output for production deployment
output: 'standalone',
// * Privacy-first: Disable analytics and telemetry
productionBrowserSourceMaps: false,
experimental: {
optimizePackageImports: ['react-icons'],
},
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'www.google.com',
pathname: '/s2/favicons**',
},
{
protocol: 'https',
hostname: 'ciphera.net',
},
],
},
async headers() {
return [
{
// * Prevent CDN/browser from serving stale HTML after deploys.
// * Static assets (/_next/static/*) are content-hashed and cached separately by Next.js.
source: '/((?!_next/static|_next/image).*)',
headers: [
{ key: 'Cache-Control', value: 'no-cache, must-revalidate' },
],
},
{
source: '/(.*)',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()',
},
{ key: 'X-XSS-Protection', value: '1; mode=block' },
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload',
},
{ key: 'Content-Security-Policy', value: cspDirectives },
],
},
]
},
async redirects() {
return [
{
source: '/dashboard',
destination: '/',
permanent: false,
},
]
},
async rewrites() {
return [
{
source: '/docs',
destination: 'https://ciphera-e9ed055e.mintlify.dev/docs',
},
{
source: '/docs/:path*',
destination: 'https://ciphera-e9ed055e.mintlify.dev/docs/:path*',
},
]
},
}
export default withPWA(nextConfig)