<-- all posts
08. Jun 2025

Adding a Content Security Policy in Laravel without using a package

Content Security Policy (CSP) is a critical security feature that helps prevent cross-site scripting (XSS) attacks by controlling which resources can be loaded by a web page. In this guide, we’ll create a Laravel middleware to enforce CSP headers dynamically, ensuring security while allowing scripts and styles to be executed safely. When it comes to implementing a CSP in Laravel, the package spatie/laravel-csp is almost always recommended. Below, we show how to achieve this without using an additional package.

Step 1: Create Middleware

We will create a middleware called AddContentSecurityPolicyHeaders.php that dynamically generates a nonce for each request. This nonce will be shared with Blade templates and used in inline scripts and styles.

Create a new middleware file in app/Http/Middleware/

php artisan make:middleware AddContentSecurityPolicyHeaders

and add the following code:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;

class AddContentSecurityPolicyHeaders
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // Generate CSP nonce using Laravel Vite
        Vite::useCspNonce();
        $cspNonce = Vite::cspNonce();
        
        // Share nonce with Blade views
        View::share('cspNonce', $cspNonce);

        // Define Content Security Policy header
        $cspHeader = "script-src 'nonce-{$cspNonce}' ; " .
                     "object-src 'none'; " .
                     "base-uri 'self'; " .
                     "style-src 'nonce-{$cspNonce}' ; ";

        // Apply CSP headers to the response
        return $next($request)->withHeaders([
            'Content-Security-Policy' => $cspHeader,
        ]);
    }
}

Step 2: Register Middleware in bootstrap/app.php

To ensure that the middleware is applied globally, register it in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->prepend(
        AddContentSecurityPolicyHeaders::class
    );
})

This ensures that every request includes the appropriate CSP headers.

Step 3: Use CSP Nonce in Blade Templates

To allow inline scripts and styles while maintaining security, use the CSP nonce in your Blade views:

<script nonce="{{ $cspNonce }}">
    console.log("Secure script executed!");
</script>

Similarly, you can use the nonce for inline styles:

<style nonce="{{ $cspNonce }}">
    body { background-color: #f0f0f0; }
</style>

Implementing a strong CSP policy is an essential step in securing your Laravel application against XSS attacks. By dynamically generating a nonce and enforcing CSP headers, you ensure that only trusted scripts and styles are executed.

Variants and Testing

Here is a code example for the case that you want to disable CSP locally and, for example, use a different CSP definition in the admin area than in the public section of your website. It also shows how you can allow certain external sources.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;

class AddContentSecurityPolicyHeaders
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        Vite::useCspNonce();
        $cspNonce = Vite::cspNonce();
        View::share('cspNonce', $cspNonce);

        if (app()->environment('local') || $request->is('admin/log-viewer')) {
            $cspHeader = "";
        } elseif ($request->is('admin*')) {
            $cspHeader = "script-src 'nonce-{$cspNonce}' 'strict-dynamic'; " .
            "object-src 'none'; " .
            "base-uri 'self'; " .
            "form-action 'self'";
        } else {
            $cspHeader = "script-src 'nonce-{$cspNonce}' https://www.googletagmanager.com ; " .
            "object-src 'none'; " .
            "base-uri 'self'; " .
            "style-src 'self' 'nonce-{$cspNonce}' https://cdnjs.cloudflare.com https://fonts.googleapis.com;" .
          "form-action 'self';";
        }

        return $next($request)->withHeaders([
            'Content-Security-Policy' => $cspHeader,
        ]);
    }
}

For testing, we recommend the CSP Evaluator (also available as a Chrome Extension): csp-evaluator.withgoogle.com

Contact - Data Protection - Imprint