-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnext.config.js
More file actions
131 lines (120 loc) · 3.5 KB
/
next.config.js
File metadata and controls
131 lines (120 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// Environment variables
env: {
GOOGLE_ANALYTICS_ID: 'GTM-W24L468',
GTM_ID: 'GTM-W24L468',
},
// Performance optimizations
compress: true, // Enable gzip compression
// Speed up static generation - don't wait for all pages
staticPageGenerationTimeout: 60,
// Image optimization - reduced sizes for faster builds
images: {
formats: ['image/webp'], // Remove avif - slower to generate
deviceSizes: [640, 1080, 1920], // Reduced from 8 sizes to 3
imageSizes: [64, 256, 384],
minimumCacheTTL: 86400, // 24 hours
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
// Compiler optimizations
compiler: {
styledComponents: {
ssr: true,
displayName: true,
fileName: true,
},
removeConsole: process.env.NODE_ENV === 'production' ? {
exclude: ['error', 'warn'],
} : false,
},
// Experimental features for better performance
experimental: {
optimizePackageImports: ['lucide-react', 'react-icons', '@react-three/fiber', '@react-three/drei', 'three'],
// Use webpack build worker for parallel processing
webpackBuildWorker: true,
// Parallelize server compilation
parallelServerCompiles: true,
parallelServerBuildTraces: true,
},
// Prevent Vercel from bundling public/ assets into serverless functions
outputFileTracingExcludes: {
'*': [
'public/images/**',
'public/background/**',
'public/splats/**',
],
},
// Redirects for SEO: fix broken /docs/articles/ URLs and normalize domain
async redirects() {
return [
// Root Cause #3: Redirect old /docs/articles/ URLs to /articles/
{
source: '/docs/articles/:slug',
destination: '/articles/:slug',
permanent: true,
},
// Catch /docs/articles without a slug too
{
source: '/docs/articles',
destination: '/articles',
permanent: true,
},
// Fix for renamed article: ai-kill-switch → ai-kill-switch-postmortem
{
source: '/articles/ai-kill-switch',
destination: '/articles/ai-kill-switch-postmortem',
permanent: true,
},
// Redirect old hire-me page to current-work
{
source: '/hire-me',
destination: '/current-work',
permanent: true,
},
];
},
// Headers for caching and security
async headers() {
const securityHeaders = [
{ 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=()' },
];
return [
{
source: '/:path*',
headers: securityHeaders,
},
{
source: '/api/:path*',
headers: [
{ key: 'X-Robots-Tag', value: 'noindex, nofollow' },
],
},
{
source: '/:all*(svg|jpg|jpeg|png|gif|ico|webp|avif)',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
{
source: '/_next/static/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
];
},
}
module.exports = nextConfig