Security Headers
HTTP security headers tell the browser how to handle your content. They are low-effort and high-impact — a couple of hours of work that closes off a whole class of browser-based 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 sensible 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 class 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:
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\App\Http\Middleware\SecurityHeaders::class);
})
That's all you need in Laravel 12. The bootstrap/app.php file is the single place to configure middleware for your application.
Using a Package
The bepsvpt/secure-headers package takes 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:
- securityheaders.com — scans your site and grades each header
- Mozilla Observatory — broader security scan including headers
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-Securityuntil you are fully committed to HTTPS. A highmax-ageis difficult to undo. - If you use a CDN like Cloudflare, headers such as 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.