philiprehberger / laravel-security-headers
Laravel middleware for comprehensive security headers including CSP with nonce support, HSTS, and Permissions-Policy
Package info
github.com/philiprehberger/laravel-security-headers
pkg:composer/philiprehberger/laravel-security-headers
Fund package maintenance!
Requires
- php: ^8.2
- illuminate/http: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- illuminate/view: ^11.0|^12.0
Requires (Dev)
- larastan/larastan: ^2.0|^3.0
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0
- phpstan/extension-installer: ^1.4
- phpunit/phpunit: ^11.0
README
Laravel middleware for comprehensive security headers including CSP with nonce support, HSTS, and Permissions-Policy.
Requirements
- PHP 8.2+
- Laravel 11 or 12
Installation
composer require philiprehberger/laravel-security-headers
Laravel auto-discovery registers the service provider automatically.
Usage
Publishing the Config
php artisan vendor:publish --tag=security-headers-config
This copies config/security-headers.php into your application's config/ directory.
Registering the Middleware
Laravel 11+ (bootstrap/app.php)
use PhilipRehberger\SecurityHeaders\SecurityHeaders; ->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [ SecurityHeaders::class, ]); })
Laravel 10 and earlier (app/Http/Kernel.php)
protected $middlewareGroups = [ 'web' => [ // ... \PhilipRehberger\SecurityHeaders\SecurityHeaders::class, ], ];
Using the CSP Nonce in Blade
The nonce is shared to every view under the variable name configured in csp.nonce_view_variable (default: cspNonce).
<script nonce="{{ $cspNonce }}"> console.log('Inline script allowed by CSP nonce'); </script> <style nonce="{{ $cspNonce }}"> /* Inline styles allowed by CSP nonce */ </style>
Accessing the Nonce in PHP
$nonce = $request->attributes->get('csp_nonce');
Configuration Reference
// config/security-headers.php return [ 'hsts' => [ // Set SECURITY_HEADERS_HSTS=true in .env (only when fully on HTTPS) 'enabled' => env('SECURITY_HEADERS_HSTS', false), 'max_age' => 31536000, 'include_subdomains' => true, ], 'csp' => [ 'enabled' => true, 'report_only' => false, // send Report-Only header instead of enforcing 'nonce_view_variable' => 'cspNonce', 'nonce_request_attribute' => 'csp_nonce', 'unsafe_eval' => true, // include 'unsafe-eval' in script-src 'unsafe_inline' => true, // include 'unsafe-inline' in style-src // Extra sources merged into each directive 'script_src' => [], // appended to: 'self' 'nonce-...' (+ 'unsafe-eval' if enabled) 'style_src' => [], // appended to: 'self' (+ 'unsafe-inline' if enabled) 'img_src' => [], // appended to: 'self' data: blob: 'font_src' => [], // appended to: 'self' data: 'connect_src' => [], // appended to: 'self' 'frame_ancestors' => ["'self'"], 'form_action' => ["'self'"], // Optional CSP reporting (omitted when null) 'report_uri' => null, // appended as `report-uri <uri>` 'report_to' => null, // appended as `report-to <group>` ], // Set to null to omit the header entirely 'x_content_type_options' => 'nosniff', 'x_frame_options' => 'SAMEORIGIN', 'x_xss_protection' => '1; mode=block', 'referrer_policy' => 'strict-origin-when-cross-origin', 'permissions_policy' => 'geolocation=(), camera=(), microphone=(), payment=()', 'permitted_cross_domain_policies' => 'none', // X-Permitted-Cross-Domain-Policies (null/false to omit) 'vite' => [ 'enabled' => true, 'dev_server' => 'http://127.0.0.1:5173', ], ];
Hardening the CSP
By default, 'unsafe-eval' is included in script-src and 'unsafe-inline' is included in style-src for broad compatibility. You can disable these for stricter security:
'csp' => [ 'unsafe_eval' => false, // removes 'unsafe-eval' from script-src 'unsafe_inline' => false, // removes 'unsafe-inline' from style-src ],
When unsafe_inline is disabled, all inline styles must use the CSP nonce. When unsafe_eval is disabled, eval() and related JavaScript features are blocked.
Hardcoded CSP Directives
The following directives are always included and cannot be changed via config:
| Directive | Value | Purpose |
|---|---|---|
default-src |
'self' |
Fallback for all resource types |
base-uri |
'self' |
Prevents <base> tag hijacking |
object-src |
'none' |
Blocks Flash/Java embeds |
Customization Examples
Allow an external CDN for scripts
'csp' => [ 'script_src' => ['https://cdn.jsdelivr.net'], ],
Allow external font providers
'csp' => [ 'font_src' => ['https://fonts.bunny.net', 'https://fonts.gstatic.com'], 'style_src' => ['https://fonts.bunny.net'], ],
Allow WebSocket connections to a production server
'csp' => [ 'connect_src' => ['wss://ws.example.com'], ],
Allow forms to post to a subdomain
'csp' => [ 'form_action' => ["'self'", 'https://portal.example.com'], ],
Enable HSTS in production via environment variable
SECURITY_HEADERS_HSTS=true
Enable Report-Only mode to test your CSP without enforcing it
'csp' => [ 'report_only' => true, ],
When report_only is true, the middleware sends Content-Security-Policy-Report-Only instead of Content-Security-Policy. This lets browsers report violations without blocking resources, which is useful when rolling out a new policy.
Send CSP violation reports to an endpoint
'csp' => [ 'report_uri' => 'https://example.com/csp-report', 'report_to' => 'csp-endpoint', ],
When set, report-uri <uri> and/or report-to <group> are appended to the CSP header. Both are omitted by default.
Configure X-Permitted-Cross-Domain-Policies
'permitted_cross_domain_policies' => 'none', // default
Controls the X-Permitted-Cross-Domain-Policies header which restricts Adobe Flash/PDF cross-domain policy files. Set to null or false to omit the header.
Use the CspDirective enum for type-safe directive names
use PhilipRehberger\SecurityHeaders\CspDirective; $directive = CspDirective::ScriptSrc; // 'script-src' $directive = CspDirective::from('style-src'); // CspDirective::StyleSrc
Remove a header you do not need
'x_xss_protection' => null,
API
Middleware
| Class | Description |
|---|---|
SecurityHeaders |
Middleware that injects all configured security headers into each response and generates a per-request CSP nonce |
Enums
| Enum | Description |
|---|---|
CspDirective |
Backed string enum with cases for common CSP directives (DefaultSrc, ScriptSrc, StyleSrc, ImgSrc, FontSrc, ConnectSrc, MediaSrc, FrameSrc, BaseUri, FormAction, FrameAncestors) |
Service Provider
Auto-discovered via Laravel's package discovery. Registers the middleware and publishes the config file.
Blade Variable
| Variable | Description |
|---|---|
$cspNonce |
Per-request CSP nonce shared to all Blade views (name configurable via csp.nonce_view_variable) |
Request Attribute
| Attribute | Description |
|---|---|
csp_nonce |
Per-request CSP nonce accessible via $request->attributes->get('csp_nonce') |
Development
composer install vendor/bin/phpunit vendor/bin/pint --test vendor/bin/phpstan analyse
Support
If you find this project useful: