Files
syndarix/frontend/next.config.ts
Felipe Cardoso efbe91ce14 fix(frontend): use configurable backend URL in Next.js rewrite
The rewrite was using 'http://backend:8000' which only resolves inside
Docker network. When running Next.js locally (npm run dev), the hostname
'backend' doesn't exist, causing ENOTFOUND errors.

Now uses NEXT_PUBLIC_API_BASE_URL env var with fallback to localhost:8000
for local development. In Docker, set NEXT_PUBLIC_API_BASE_URL=http://backend:8000.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 09:22:44 +01:00

96 lines
3.2 KiB
TypeScript
Executable File

import type { NextConfig } from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
// Initialize next-intl plugin with i18n request config path
const withNextIntl = createNextIntlPlugin('./src/lib/i18n/request.ts');
/**
* Security Headers Configuration (OWASP 2025 recommendations)
*
* References:
* - https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html
* - https://nextjs.org/docs/app/api-reference/config/next-config-js/headers
*
* Note: X-XSS-Protection is intentionally omitted (deprecated, removed from browsers)
* Note: Strict CSP requires dynamic rendering with nonces - not implemented here
*/
const securityHeaders = [
{
// Prevents clickjacking by denying iframe embedding
// Also handled by CSP frame-ancestors, but X-Frame-Options provides legacy browser support
key: 'X-Frame-Options',
value: 'DENY',
},
{
// Prevents MIME type sniffing attacks
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
// Controls how much referrer information is sent with requests
// 'strict-origin-when-cross-origin' is the recommended balance of privacy and functionality
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
// Disables browser features that aren't needed
// Add features back as needed: camera=self, microphone=self, etc.
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), browsing-topics=()',
},
{
// Basic CSP that works with inline scripts/styles (required for theme init and Tailwind)
// For strict CSP with nonces, use proxy.ts with dynamic rendering
// frame-ancestors 'none' duplicates X-Frame-Options for modern browsers
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'", // unsafe-eval needed for MSW in dev
"style-src 'self' 'unsafe-inline'", // Required for Tailwind and styled components
"img-src 'self' blob: data: https:", // Allow images from HTTPS sources
"font-src 'self'",
"connect-src 'self' http://localhost:* ws://localhost:*", // API + HMR websocket
"worker-src 'self' blob:", // Required for MSW service worker in demo mode
"child-src 'self' blob:", // For service worker registration
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'none'",
].join('; '),
},
];
const nextConfig: NextConfig = {
output: 'standalone',
// Security headers applied to all routes
async headers() {
return [
{
// Apply to all routes
source: '/(.*)',
headers: securityHeaders,
},
];
},
// Proxy API requests to backend
// Use NEXT_PUBLIC_API_BASE_URL for the destination (defaults to localhost for local dev)
async rewrites() {
const backendUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000';
return [
{
source: '/api/:path*',
destination: `${backendUrl}/api/:path*`,
},
];
},
// Production optimizations
reactStrictMode: true,
// Note: SWC minification is default in Next.js 16
};
// Wrap config with next-intl plugin
export default withNextIntl(nextConfig);