Files
school_compare/nextjs-app/next.config.js
T
TudorandClaude Opus 4.8 9556595800
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m2s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 48s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 8s
feat(security): allow analytics subdomain to iframe the site
Replace X-Frame-Options: SAMEORIGIN with a CSP frame-ancestors directive
so analytics.schoolcompare.co.uk (Umami heatmap/recorder) can embed the
site while all other origins stay blocked. X-Frame-Options cannot allow a
specific sibling subdomain (ALLOW-FROM is deprecated/ignored by modern
browsers), so frame-ancestors is the correct replacement.

Also update the nginx snippet in DEPLOYMENT.md to match, so the reverse
proxy doesn't re-inject a conflicting X-Frame-Options header.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 22:50:44 +01:00

88 lines
2.4 KiB
JavaScript

/** @type {import('next').NextConfig} */
const nextConfig = {
// Enable standalone output for Docker
output: 'standalone',
// The /api/* and /sitemap.xml proxies to the FastAPI backend are route
// handlers (app/api/[...path]/route.ts, app/sitemap.xml/route.ts) rather
// than rewrites, so the backend host is read from FASTAPI_URL at runtime
// instead of being baked into the build.
// Image optimization
images: {
remotePatterns: [
{ protocol: 'https', hostname: '*.tile.openstreetmap.org' },
{ protocol: 'https', hostname: 'tile.openstreetmap.org' },
{ protocol: 'https', hostname: 'cdnjs.cloudflare.com' },
],
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 31536000,
},
// Performance optimizations
compiler: {
// Remove console logs in production
removeConsole: process.env.NODE_ENV === 'production',
},
// Compression
compress: true,
// React strict mode for better error detection
reactStrictMode: true,
// Power optimizations
poweredByHeader: false,
// Production source maps (disable for smaller bundles)
productionBrowserSourceMaps: false,
// Experimental features for performance
experimental: {
// Optimize package imports
optimizePackageImports: ['chart.js', 'react-chartjs-2', 'leaflet'],
},
// Headers for caching and security
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-DNS-Prefetch-Control',
value: 'on',
},
{
// Allow the analytics subdomain (Umami heatmap/recorder) to embed
// the site while blocking all other origins. frame-ancestors is the
// modern replacement for X-Frame-Options, which cannot allow a
// specific sibling subdomain (ALLOW-FROM is deprecated/ignored).
key: 'Content-Security-Policy',
value: "frame-ancestors 'self' https://analytics.schoolcompare.co.uk",
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin',
},
],
},
{
source: '/favicon.svg',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
];
},
};
module.exports = nextConfig;