How to get Forge hosted Laravel application Security Headers perfect

Security

To get your security Headers perfect, you'll need the right tools, starting with a scanner.

Use this scanner, before and then after:

https://securityheaders.com/

Before:

security-headers-report.png 82.91 KB
Changes required (Laravel 12):

Content security is a can of worms but luckily the amazing Spatie has already solved this here.

composer require spatie/laravel-csp

Publish the config-file with:

php artisan vendor:publish --tag=csp-config

This config file is going to make or break your production website. Make sure you have a way to test it!

In our case, where we extensively use Filament, we had to add BunnyFonts:class and quickly uncomment some other directives:

'presets' => [
        Spatie\Csp\Presets\Basic::class,
        Spatie\Csp\Presets\BunnyFonts::class,
    ],    /**
     * Register additional global CSP directives here.
     */    'directives' => [
        [Directive::SCRIPT, [Keyword::UNSAFE_EVAL, Keyword::UNSAFE_INLINE]],
    ],

This did *NOT* work. The reason is you have incredibly carefully test each and every aspect of your website to make sure it's okay.

Here is the `SecureHeaders.php` middleware class:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class SecureHeaders
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);
        $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
        $response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
        $response->headers->set('Permissions-Policy', 'autoplay=(self), camera=(), encrypted-media=(self), fullscreen=(), geolocation=(self), gyroscope=(self), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=(self), usb=()');
        return $response;
    }
}
lang-php

Once you have a working config, you can add the middlewares:

 ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'role' => RoleMiddleware::class,
            'permission' => PermissionMiddleware::class,
            'role_or_permission' => RoleOrPermissionMiddleware::class,
        ]);
        // uncomment after testing each and every aspect of the website!
        // $middleware->append(AddCspHeaders::class);
        $middleware->append(SecureHeaders::class);
    })
lang-php

After (many hours of fiddling):

security-headers-pass.png 83.21 KB
See these blogs that helped me out: