paulund
#laravel #security #best-practices

Security Headers

HTTP security headers instruct browsers on how to handle your content. They are a low-effort, high-impact addition to any web application. Without them, your application is vulnerable to a range of attacks including clickjacking, cross-site scripting, and content injection.

Key Headers

Content-Security-Policy (CSP)

CSP tells the browser which sources of scripts, styles, images, and other resources are legitimate. It prevents injected scripts from running even if an attacker manages to insert them into your HTML.

A strict example:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';

Start with Content-Security-Policy-Report-Only to collect violations without blocking anything, then tighten the policy once you understand your application's legitimate sources.

X-Frame-Options

Prevents your pages from being embedded in an <iframe> on another domain, protecting against clickjacking:

X-Frame-Options: SAMEORIGIN

X-Content-Type-Options

Stops browsers from guessing the MIME type of a response. Without this, a browser might execute a .txt file as JavaScript if the content looks like a script:

X-Content-Type-Options: nosniff

Referrer-Policy

Controls how much referrer information is sent when navigating away from your site. strict-origin-when-cross-origin is a good default:

Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policy

Restricts which browser features (camera, microphone, geolocation) the page can access. Deny everything your application does not need:

Permissions-Policy: camera=(), microphone=(), geolocation=()

Strict-Transport-Security (HSTS)

Forces browsers to use HTTPS for all future requests to your domain. Only add this once your site is fully on HTTPS:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Adding Headers in Laravel Middleware

Create a middleware to attach security headers to every response:

php artisan make:middleware SecurityHeaders
namespace App\Http\Middleware;

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

class SecurityHeaders
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->headers->set('X-Frame-Options', 'SAMEORIGIN');
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
        $response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
        $response->headers->set(
            'Content-Security-Policy',
            "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
        );

        return $response;
    }
}

Register it globally in bootstrap/app.php (Laravel 11+):

->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\App\Http\Middleware\SecurityHeaders::class);
})

Or in app/Http/Kernel.php (Laravel 10 and below):

protected $middleware = [
    // ...
    \App\Http\Middleware\SecurityHeaders::class,
];

Using a Package

The bepsvpt/secure-headers package provides a configuration-driven approach with sensible defaults:

composer require bepsvpt/secure-headers
php artisan vendor:publish --tag=secure-headers

Configure headers in config/secure-headers.php and add the middleware to your stack. The package handles edge cases like removing headers that should not be present in certain response types.

Testing Your Headers

After deploying, check your headers with:

Both tools give an A-F grade and explain what each header does and why it matters.

Tips

  • Start with report-only CSP and monitor violations before enforcing.
  • Do not add Strict-Transport-Security until you are fully committed to HTTPS — a high max-age is difficult to undo.
  • If you use a CDN (Cloudflare, etc.), some headers like HSTS and CSP can be configured at the CDN layer rather than in Laravel.
  • Review your CSP whenever you add a new third-party script or font — CDN sources need to be explicitly allowed.