headers; $headers->set('X-Frame-Options', 'DENY'); $headers->set('X-Content-Type-Options', 'nosniff'); $headers->set('Referrer-Policy', 'strict-origin-when-cross-origin'); $headers->set( 'Permissions-Policy', 'camera=(), microphone=(), geolocation=(), interest-cohort=()' ); // Livewire/Alpine need inline + eval; Reverb rides ws/wss. $scriptSrc = ["'self'", "'unsafe-eval'", "'unsafe-inline'"]; $styleSrc = ["'self'", "'unsafe-inline'"]; $connectSrc = ["'self'", 'ws:', 'wss:']; $fontSrc = ["'self'"]; // In dev, Vite serves the bundle + HMR from a SEPARATE origin (e.g. // http://host:5173). A same-origin CSP would block it and break the app, // so allow the running dev-server origin (from public/hot) outside prod. // In production assets are same-origin (/build) and this branch is inert. if (! app()->isProduction() && is_file(public_path('hot'))) { $dev = trim((string) file_get_contents(public_path('hot'))); if ($dev !== '') { $devWs = preg_replace('#^http#', 'ws', $dev); array_push($scriptSrc, $dev); array_push($styleSrc, $dev); array_push($fontSrc, $dev); array_push($connectSrc, $dev, $devWs); } } $headers->set('Content-Security-Policy', implode('; ', [ "default-src 'self'", 'script-src '.implode(' ', $scriptSrc), 'style-src '.implode(' ', $styleSrc), 'connect-src '.implode(' ', $connectSrc), "img-src 'self' data:", 'font-src '.implode(' ', $fontSrc), "base-uri 'self'", "form-action 'self'", "frame-ancestors 'none'", ])); // Only assert HSTS over an already-secure connection so we never // pin clients to HTTPS on a plain-HTTP dev/LAN deployment. if ($request->isSecure()) { $headers->set( 'Strict-Transport-Security', 'max-age=31536000; includeSubDomains' ); } return $response; } }