129 lines
3.1 KiB
TypeScript
129 lines
3.1 KiB
TypeScript
import type { NextConfig } from "next";
|
|
|
|
const nextConfig: NextConfig = {
|
|
output: "standalone",
|
|
|
|
images: {
|
|
remotePatterns: [
|
|
{
|
|
protocol: "https",
|
|
hostname: "crafatar.com",
|
|
pathname: "/**",
|
|
},
|
|
{
|
|
protocol: "https",
|
|
hostname: "mc-heads.net",
|
|
pathname: "/**",
|
|
},
|
|
{
|
|
protocol: "https",
|
|
hostname: "visage.surgeplay.com",
|
|
pathname: "/**",
|
|
},
|
|
{
|
|
protocol: "https",
|
|
hostname: "minotar.net",
|
|
pathname: "/**",
|
|
},
|
|
],
|
|
},
|
|
|
|
async headers() {
|
|
const cspDirectives = [
|
|
"default-src 'self'",
|
|
// Scripts: self + strict-dynamic (Turbopack compatible)
|
|
"script-src 'self' 'unsafe-inline'",
|
|
// Styles: self + unsafe-inline (required for Tailwind/CSS-in-JS in Next.js)
|
|
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
// Fonts
|
|
"font-src 'self' https://fonts.gstatic.com data:",
|
|
// Images: self + data URIs + MC avatar APIs
|
|
"img-src 'self' data: blob: https://crafatar.com https://mc-heads.net https://visage.surgeplay.com https://minotar.net",
|
|
// Connect: self + WebSocket for Socket.io
|
|
"connect-src 'self' ws: wss:",
|
|
// Frames: allow same-origin (BlueMap) + configurable origins
|
|
"frame-src 'self'",
|
|
// Frame ancestors: only same origin (replaces X-Frame-Options)
|
|
"frame-ancestors 'self'",
|
|
// Workers: self + blob (xterm.js, Monaco)
|
|
"worker-src 'self' blob:",
|
|
// Media
|
|
"media-src 'self'",
|
|
// Manifest
|
|
"manifest-src 'self'",
|
|
// Object: none
|
|
"object-src 'none'",
|
|
// Base URI
|
|
"base-uri 'self'",
|
|
// Form actions
|
|
"form-action 'self'",
|
|
// Upgrade insecure requests in production
|
|
...(process.env.NODE_ENV === "production"
|
|
? ["upgrade-insecure-requests"]
|
|
: []),
|
|
].join("; ");
|
|
|
|
const securityHeaders = [
|
|
{
|
|
key: "Content-Security-Policy",
|
|
value: cspDirectives,
|
|
},
|
|
{
|
|
key: "X-Frame-Options",
|
|
value: "SAMEORIGIN",
|
|
},
|
|
{
|
|
key: "X-Content-Type-Options",
|
|
value: "nosniff",
|
|
},
|
|
{
|
|
key: "Referrer-Policy",
|
|
value: "strict-origin-when-cross-origin",
|
|
},
|
|
{
|
|
key: "Permissions-Policy",
|
|
value: "camera=(), microphone=(), geolocation=(), browsing-topics=()",
|
|
},
|
|
{
|
|
key: "X-DNS-Prefetch-Control",
|
|
value: "on",
|
|
},
|
|
{
|
|
key: "Strict-Transport-Security",
|
|
value: "max-age=63072000; includeSubDomains; preload",
|
|
},
|
|
];
|
|
|
|
return [
|
|
{
|
|
source: "/(.*)",
|
|
headers: securityHeaders,
|
|
},
|
|
];
|
|
},
|
|
|
|
// Turbopack config (Next.js 16 default bundler)
|
|
turbopack: {},
|
|
|
|
// Disable powered-by header
|
|
poweredByHeader: false,
|
|
|
|
// Enable strict mode
|
|
reactStrictMode: true,
|
|
|
|
// Compress responses
|
|
compress: true,
|
|
|
|
// Server actions body size limit
|
|
experimental: {
|
|
serverActions: {
|
|
bodySizeLimit: "10mb",
|
|
},
|
|
},
|
|
|
|
// Remove 'import crypto' at top — not needed in static headers
|
|
|
|
};
|
|
|
|
export default nextConfig;
|