Content Security Policy CSP for Laravel Apps

Khimananda Oli 9 min read Security
Content Security Policy CSP for Laravel Apps

By Khimananda Oli | Last reviewed: August 2026

Browser-side attacks remain the most common entry point for compromising web applications, and implementing a robust Content Security Policy CSP for Laravel Apps is your strongest defense against Cross-Site Scripting (XSS) and data injection. While Laravel provides excellent server-side validation, it cannot prevent malicious scripts injected via compromised third-party libraries or user-generated content from executing in the browser. This guide walks you through configuring, testing, and enforcing CSP headers specifically tailored for Laravel’s Blade templating and asset pipeline without breaking your production environment.

Browser RequestHTML + Inline ScriptLaravel CSP MiddlewareValidates Nonce / HashBlocks Unauthorized ExecSafe RenderOnly Trusted Scripts RunViolation ReportPOST /csp-report (Log)
Figure 1: How Content Security Policy CSP for Laravel Apps intercepts requests at the middleware layer to block unauthorized script execution while logging violations.

How do you configure Content Security Policy CSP for Laravel Apps safely?

The safest approach to deploying CSP in any PHP framework is to treat it as an observability problem before treating it as a security control. A common mistake I see teams make—especially when rushing to satisfy audit requirements like SOC 2 or ISO 27001—is flipping CSP to enforcement mode immediately. This inevitably breaks payment gateways, analytics, or embedded widgets because modern web apps have complex dependency graphs that are rarely fully documented. Instead, follow this proven sequence: define a baseline policy, deploy in report-only mode, collect violation data for at least two weeks, refine allowlists based on real traffic, and only then enforce.

Laravel does not include CSP middleware out of the box. While you could set headers directly in Nginx or Apache, doing so prevents you from using dynamic nonces—a critical requirement for allowing inline scripts securely. The community-standard solution is spatie/laravel-csp, which integrates deeply with Blade and generates per-request nonces automatically. Install it via Composer and publish the configuration:

composer require spatie/laravel-csp
php artisan vendor:publish --tag=csp-config

This creates config/csp.php and a default policy class. Never edit the published config to add domains directly; instead, create a custom policy class that extends Spatie’s base policy. This keeps your rules version-controlled, testable, and reviewable in pull requests—a practice aligned with shifting security left in CI/CD.

Create a Custom Policy Class

Generate a new policy tailored to your application’s actual dependencies. Avoid copying generic examples; every directive should map to a verified business need:

<?php

namespace App\Csp;

use Spatie\Csp\Policies\Policy;
use Spatie\Csp\Directives;

class LaravelAppPolicy extends Policy
{
    public function configure()
    {
        $this
            ->addDirective(Directives::BASE, "'self'")
            ->addDirective(Directives::SCRIPT, [
                "'self'",
                "'nonce-{nonce}'", // Auto-replaced by middleware
                'https://js.stripe.com',
                'https://cdn.jsdelivr.net',
            ])
            ->addDirective(Directives::STYLE, [
                "'self'",
                "'nonce-{nonce}'",
                'https://fonts.googleapis.com',
            ])
            ->addDirective(Directives::FONT, [
                "'self'",
                'https://fonts.gstatic.com',
            ])
            ->addDirective(Directives::IMG, [
                "'self'",
                'data:',
                'https://*.amazonaws.com', // S3 uploads
            ])
            ->addDirective(Directives::CONNECT, [
                "'self'",
                'https://api.stripe.com',
                'https://sentry.io',
            ])
            ->addNonceForDirective(Directives::SCRIPT)
            ->addNonceForDirective(Directives::STYLE);
    }
}

Register this policy in config/csp.php under the policies key and ensure the middleware is applied globally or to specific route groups. For applications serving both a marketing site and an authenticated app, consider separate policies—the admin panel may need stricter controls than the public-facing pages.

Why are nonces essential for Laravel CSP instead of hashes?

You might wonder why we use nonces rather than SHA-256 hashes for inline scripts. Hashes work well for static content but become unmanageable in Laravel because Blade templates often contain dynamic data embedded in JavaScript (e.g., @json($user)). Every change to that data invalidates the hash, forcing you to recompute and redeploy. Nonces solve this by generating a unique cryptographic token per HTTP response that applies to all inline blocks marked with that nonce.

Spatie’s package handles nonce generation and injection automatically when you use its Blade directives. Replace raw <script> tags with:

@cspScriptTag
    window.APP_CONFIG = @json($config);
@endcspScriptTag

@cspStyleTag
    .dynamic-theme { color: {{ $themeColor }}; }
@endcspStyleTag

If you’re using Vite (standard in Laravel 11+), the build tool automatically adds nonces to generated asset tags when the CSP middleware is active. Verify this by inspecting the rendered HTML in production—you should see nonce="..." attributes on every <script> and <link> tag. Missing nonces indicate either misconfigured middleware or cached views; run php artisan view:clear and check your production deployment checklist for cache-busting steps.

Nonce Approach (Recommended)Per-request random tokenWorks with dynamic @json() dataAuto-injected by Vite & BladeNo rebuild needed on content change⚠ Requires HTTPS for securityHash Approach (Limited)SHA-256 of exact script contentBreaks on any dynamic variableManual computation requiredRebuild/deploy on every change✓ Works over HTTP (rarely useful)
Figure 2: Why nonces are preferred over hashes for Content Security Policy CSP for Laravel Apps with dynamic Blade-rendered content.

How do you monitor CSP violations without disrupting users?

Enforcement without monitoring is operational negligence. Before switching from report-only to enforced mode, you must establish a feedback loop. Configure your policy to send violation reports to an internal endpoint rather than an external service—this avoids leaking sensitive URL parameters or page structure to third parties and satisfies data residency concerns relevant to Nepali fintech or healthcare clients.

Add a dedicated route and controller to ingest CSP reports:

// routes/web.php
Route::post('/csp-report', [CspReportController::class, 'store'])
    ->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);

// app/Http/Controllers/CspReportController.php
public function store(Request $request)
{
    Log::channel('csp')->warning('CSP Violation', [
        'document-uri' => $request->input('document-uri'),
        'violated-directive' => $request->input('violated-directive'),
        'blocked-uri' => $request->input('blocked-uri'),
        'source-file' => $request->input('source-file'),
        'user-agent' => $request->userAgent(),
    ]);

    return response()->noContent();
}

Create a dedicated log channel in config/logging.php to isolate CSP violations from application logs. In production, ship these to your centralized logging stack—whether that’s ELK, Grafana Loki, or CloudWatch. Set up alerts for spikes in specific directives; a sudden surge in script-src violations after a deploy usually indicates a missing nonce or a newly introduced third-party library. During my work helping teams achieve SOC 2 compliance, automated CSP violation monitoring has been invaluable evidence of continuous security control effectiveness during audits.

What are the most common CSP pitfalls in Laravel production environments?

Even experienced teams stumble on these issues when hardening Laravel apps. Understanding them upfront saves hours of debugging:

  • Overusing 'unsafe-inline': This defeats the purpose of CSP. If you’re tempted to add it because “nothing works,” you’ve skipped the reporting phase. Go back, collect violations, and fix the root cause.
  • Ignoring CDN subresource integrity: When allowing cdn.jsdelivr.net, pair it with SRI hashes in your HTML. CSP allows the domain, but SRI ensures the file hasn’t been tampered with—a defense-in-depth practice I recommend for all security-hardened deployments.
  • Forgetting WebSocket connections: If you use Laravel Reverb or Pusher, add wss:// schemes to connect-src. Browsers block WS/WSS upgrades silently unless explicitly allowed.
  • Caching stale policies: Always set Cache-Control: no-store on CSP headers during tuning. Browser caches can persist old report-only policies for hours, making debugging impossible.
  • Mixing meta tags and headers: Never define CSP in both <meta> and HTTP headers. Headers take precedence, and meta tags don’t support frame-ancestors or reporting. Stick to middleware-generated headers exclusively.
DirectiveRisky ValueSecure AlternativeLaravel Context
script-src'unsafe-inline''nonce-{nonce}'Use @cspScriptTag for all inline JS
style-src'unsafe-inline''nonce-{nonce}' + allowlisted fontsTailwind JIT may need nonce; prebuilt CSS doesn’t
img-src*'self' data: https://*.yourdomain.comRestrict S3/R2 buckets to known prefixes
connect-srchttps:Explicit API domains + WSS endpointsInclude Reverb/Pusher/Sentry URLs
frame-ancestors'none' (default)'self' if embedding internallyRequired for admin panels in iframes
CSP Violation LoggedIs blocked-uri trusted?YesNoAdd to AllowlistBlock & InvestigateVerify SRI / DomainDeploy Refined PolicyCheck User Input / XSSPatch Vulnerability
Figure 3: Decision workflow for handling Content Security Policy CSP for Laravel Apps violations—never blindly whitelist without verification.

When should you enforce CSP in production Laravel deployments?

Switch to enforcement only after meeting three criteria: you’ve collected at least 14 days of violation reports covering peak traffic patterns, all high-frequency violations have been resolved or explicitly accepted as risk, and you have automated alerting configured for new violation types. In practice, this means your staging environment should mirror production CSP behavior exactly—including the same third-party integrations and CDN configurations.

Update your policy class to remove report-only mode and add the upgrade-insecure-requests directive if you haven’t already enforced HTTPS everywhere. Test thoroughly across browsers; Safari historically has quirks with nonce handling that require fallback strategies. Document your final policy in your security runbook and link it to your OWASP Top 10 mitigation documentation for audit readiness.

Next Steps for Securing Your Laravel Application

Implementing Content Security Policy CSP for Laravel Apps is not a one-time task—it’s an ongoing process of refinement as your application evolves. Start today by installing the Spatie package in report-only mode, instrumenting violation logging, and reviewing your current inline script usage. Within two weeks, you’ll have actionable data to build a strict, enforceable policy that genuinely protects your users without degrading functionality. If you need help designing a CSP strategy that aligns with your compliance requirements or infrastructure constraints, reach out to discuss your specific setup.

Frequently Asked Questions

Install spatie/laravel-csp via Composer and publish the configuration file. Define your policy rules in app/Csp/Policies/MyPolicy.php, then apply the middleware globally or to specific routes to enforce headers automatically without manual meta tag management.

Spatie Laravel CSP remains the standard choice for 2026 due to active maintenance and nonce support. It integrates directly with Blade directives and handles dynamic script hashing better than manual header configuration or outdated alternatives.

Yes. Configure the Vite plugin to inject nonces into generated tags and pass that value to your CSP policy class. This allows strict-dynamic policies while maintaining compatibility with HMR during local development environments.

Inline scripts violate default CSP rules unless explicitly allowed. Use the @cspNonce Blade directive on script tags or refactor code into external files. Avoid unsafe-inline as it defeats the primary purpose of implementing content security policies.

Set the report-only flag in your Spatie config or add Content-Security-Policy-Report-Only headers manually. Monitor browser console errors and violation reports to identify blocked resources without disrupting production functionality during the testing phase.

Yes, because Livewire uses inline scripts for state transfer. You must enable nonces through the official Livewire CSP integration or allow specific hashes. Blocking these scripts causes components to fail silently after initial page load.

Use a dedicated service like Sentry or Report URI rather than building custom endpoints. Laravel lacks native violation parsing, and third-party services provide aggregation, filtering, and alerting necessary to manage noise from legacy browsers and extensions.

No. Unsafe-eval permits arbitrary code execution and negates XSS protection. If a library requires eval, find an alternative or isolate it in a sandboxed iframe with its own restrictive policy instead of weakening your main application header.

Sanctum relies on cookies and XSRF tokens, not inline scripts, so CSP compatibility is generally high. Ensure your policy allows connections to your API domain and does not block the token meta tag required for AJAX requests.

Yes. Admin panels often require richer third-party integrations like charts or editors. Create distinct policy classes applied via route group middleware to maintain strict security on public pages while accommodating necessary backend tooling dependencies.

Text renders in fallback system fonts and layout shifts may occur. Add font-src directives for fonts.gstatic.com and style-src for fonts.googleapis.com to prevent visual degradation while maintaining protection against unauthorized stylesheet injection attacks.

Not directly. Search engines do not rank based on security headers alone. However, preventing XSS preserves site integrity and uptime, indirectly supporting SEO by avoiding malware flags and maintaining consistent crawlability across all pages.

Sanitize uploads using enso/sanitizer or similar libraries before storage. Serve SVGs from a separate subdomain with a restrictive CSP that blocks scripts entirely, preventing stored XSS vectors even if malicious content bypasses validation filters.

Sometimes. Cloudflare Rocket Loader injects scripts that violate strict policies. Disable it for protected routes or configure your CSP to trust Cloudflare’s specific domains. Always verify final response headers at the edge, not just locally.

Negligible. Nonce generation adds microseconds per request. The real cost is developer time managing violations. Modern PHP 8.4 and opcode caching make cryptographic random byte generation fast enough for high-traffic applications without measurable latency impact.