acolyte / laravel-security
Configurable, modern security headers middleware for Laravel applications.
Requires
- php: ^8.2 || ^8.3 || ^8.4 || ^8.5
- illuminate/contracts: ^12.0 || ^13.0
- illuminate/http: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
Requires (Dev)
- larastan/larastan: ^3.8
- laravel/pint: ^1.24
- orchestra/testbench: ^10.0 || ^11.0
- phpunit/phpunit: ^11.5
README
Laravel Security is a configurable security-headers middleware package for Laravel applications.
It helps apply modern browser security controls including Content Security Policy, HSTS, Referrer-Policy, Permissions-Policy, MIME-sniffing protection, and frame restrictions. The package provides defense-in-depth and does not replace secure application design or infrastructure configuration.
Features
- One middleware integration point with focused, testable policy objects
- HTTPS-aware HSTS with optional subdomain coverage and preload
- Enforcing and report-only Content Security Policy modes
- Deterministic policy construction with duplicate removal and input validation
- Referrer-Policy, Permissions-Policy, MIME-sniffing, and frame controls
- Response-level
X-Powered-Byremoval and validated custom headers - Publishable safe defaults, package auto-discovery, and no wildcard CORS
Requirements
| Laravel | PHP |
|---|---|
| 12.x | 8.2–8.5 |
| 13.x | 8.3–8.5 |
Only combinations exercised by CI are claimed. See Laravel's support policy when choosing a framework version.
Installation
composer require acolyte/laravel-security:^2.0
Laravel discovers LaravelSecurityServiceProvider automatically. Publish the documented configuration when you need to customize it:
php artisan vendor:publish --tag=laravel-security-config
Quick Start
Register the middleware globally in Laravel 12–13 in bootstrap/app.php:
use Acolyte\LaravelSecurity\Middleware\SecurityHeaders; use Illuminate\Foundation\Configuration\Middleware; ->withMiddleware(function (Middleware $middleware): void { $middleware->append(SecurityHeaders::class); })
Or attach SecurityHeaders::class to selected routes or route groups. Put it late enough in the middleware stack to inspect the completed response. If another middleware adds headers on its return path, middleware ordering determines which value wins.
Configuration
The published config/laravel-security.php contains comments for every option. Its defaults are equivalent to:
return [ 'enabled' => true, 'hsts' => [ 'enabled' => true, 'max_age' => 31536000, 'include_subdomains' => false, 'preload' => false, ], 'csp' => [ 'enabled' => false, 'report_only' => false, 'nonce' => [ 'enabled' => false, 'directives' => ['script-src', 'style-src'], 'request_attribute' => 'csp_nonce', ], 'directives' => [ 'default-src' => ["'self'"], 'script-src' => ["'self'"], 'style-src' => ["'self'"], 'img-src' => ["'self'", 'data:', 'https:'], 'object-src' => ["'none'"], 'base-uri' => ["'self'"], 'frame-ancestors' => ["'none'"], ], ], 'content_type_options' => 'nosniff', 'referrer_policy' => 'strict-origin-when-cross-origin', 'frame_options' => 'SAMEORIGIN', 'permissions_policy' => [ 'enabled' => true, 'policies' => [ 'camera' => [], 'microphone' => [], 'geolocation' => [], ], ], 'remove_x_powered_by' => true, 'custom_headers' => [], ];
Set a supported simple policy to false to disable it. Set top-level enabled to false to leave responses unchanged.
Security Headers
HSTS
Strict-Transport-Security tells browsers to use HTTPS for future requests. The middleware emits it only when Laravel considers the request secure. When TLS terminates at a load balancer or reverse proxy, configure Laravel's trusted proxies correctly or HTTPS detection will be wrong.
'hsts' => [ 'enabled' => true, 'max_age' => 31536000, 'include_subdomains' => true, 'preload' => false, ],
This produces Strict-Transport-Security: max-age=31536000; includeSubDomains on HTTPS responses. Preload requires at least one year and include_subdomains; invalid combinations are rejected. Preloading is difficult to reverse and can make every subdomain unreachable if one lacks valid HTTPS. Submit a domain to browser preload lists only after a deliberate operational review.
Content Security Policy
CSP restricts where a browser may load and execute resources. It is disabled by default because a generic enforcing policy can break scripts, styles, images, third-party widgets, and development tooling. Build a policy for the actual application and deploy it in report-only mode first.
The builder preserves configured directive order, removes repeated values, supports valueless directives such as upgrade-insecure-requests, rejects malformed names and values, and replaces an existing CSP header rather than appending duplicates.
For inline scripts or styles that cannot be moved to external files, enable per-request nonces:
'nonce' => [ 'enabled' => true, 'directives' => ['script-src', 'style-src'], 'request_attribute' => 'csp_nonce', ],
Use the generated value while rendering Blade:
<script nonce="{{ request()->attributes->get('csp_nonce') }}"> // Inline code allowed by this response's CSP. </script>
The nonce is generated before the response renders and reused if the middleware runs more than once. Never cache nonce-bearing HTML independently of its CSP header, and never reuse a nonce across responses.
X-Content-Type-Options
The default X-Content-Type-Options: nosniff asks supporting browsers not to reinterpret script and stylesheet MIME types. Configure content_type_options as false to disable it; arbitrary values are rejected.
Referrer-Policy
The default is Referrer-Policy: strict-origin-when-cross-origin. Configure any standardized policy value supported by the package, such as no-referrer, or use false to disable it.
Permissions-Policy
An empty allowlist disables a browser feature. self, a valid URL, and the wildcard are supported:
'permissions_policy' => [ 'enabled' => true, 'policies' => [ 'camera' => [], 'fullscreen' => ['self', 'https://video.example'], 'publickey-credentials-get' => ['*'], ], ],
This produces:
Permissions-Policy: camera=(), fullscreen=(self "https://video.example"), publickey-credentials-get=*
Browser support varies by directive; test the features your application relies on.
For a permanently enabled policy, the shorter form is also accepted: 'permissions_policy' => ['camera' => [], 'microphone' => []].
Frame Protection
CSP frame-ancestors is the modern control and should be your primary policy. X-Frame-Options remains enabled as a compatibility layer and accepts only DENY, SAMEORIGIN, or false. Keep CSP and X-Frame-Options semantically aligned.
X-Powered-By
When remove_x_powered_by is enabled, the middleware removes X-Powered-By from the outgoing Laravel response without touching unrelated headers. PHP, Nginx, Apache, a reverse proxy, CDN, or another upstream layer may inject the header after Laravel returns the response. Disable it at every responsible infrastructure layer; Laravel middleware cannot guarantee removal in every deployment.
Content Security Policy Examples
API application
An API that does not return browser-rendered HTML may not benefit from CSP. Leave it disabled while keeping the other default controls, or use a restrictive policy if API responses may be rendered:
'csp' => [ 'enabled' => true, 'report_only' => false, 'directives' => ['default-src' => ["'none'"], 'frame-ancestors' => ["'none'"]], ],
CORS is intentionally not managed by this package. Use Laravel's native config/cors.php and choose origins, methods, headers, and credential behavior for the API's trust model.
Blade application
Start with the sources the application actually uses. Avoid adding 'unsafe-inline' simply to silence violations; prefer nonces or hashes where the application architecture supports them.
'directives' => [ 'default-src' => ["'self'"], 'script-src' => ["'self'", 'https://cdn.example'], 'style-src' => ["'self'", 'https://fonts.googleapis.com'], 'font-src' => ["'self'", 'https://fonts.gstatic.com'], 'img-src' => ["'self'", 'data:', 'https:'], 'object-src' => ["'none'"], 'base-uri' => ["'self'"], 'frame-ancestors' => ["'none'"], ],
CSP report-only migration
'csp' => [ 'enabled' => true, 'report_only' => true, 'directives' => [ 'default-src' => ["'self'"], 'report-uri' => ['https://reports.example/csp'], ], ],
Review reports, remove false positives, then switch report_only to false. A report endpoint receives attacker-controlled data and should be rate-limited, validated, size-limited, and monitored.
Per-environment behavior
Call env() only inside configuration files so Laravel configuration caching remains reliable:
'csp' => [ 'enabled' => (bool) env('SECURITY_CSP_ENABLED', true), 'report_only' => (bool) env('SECURITY_CSP_REPORT_ONLY', false), // ... ],
Laravel Integration
The package is auto-discovered. For route-specific use:
use Acolyte\LaravelSecurity\Middleware\SecurityHeaders; Route::middleware(SecurityHeaders::class)->group(function (): void { Route::get('/', HomeController::class); });
Middleware can safely execute more than once: package-managed headers replace prior values instead of creating duplicate field lines. Server and proxy configuration still runs outside this middleware.
flowchart LR
A[Laravel Request] --> B[SecurityHeaders middleware]
B --> C[Package configuration]
C --> D[CSP policy]
C --> E[HSTS policy]
C --> F[Permissions policy]
C --> G[Simple and custom headers]
D --> H[Laravel Response]
E --> H
F --> H
G --> H
Loading
Custom Headers
'custom_headers' => [ 'Cross-Origin-Resource-Policy' => 'same-site', ],
Names and values must be strings; invalid field names and line breaks are rejected to prevent response-splitting mistakes. Custom headers replace an existing header of the same name. Prefer first-class options for policies the package understands because those options provide stronger validation.
Testing
composer test
composer test:coverage
composer analyse
composer format:test
composer check
Coverage requires PCOV or Xdebug. The suite uses PHPUnit and Orchestra Testbench.
Security Considerations
Security headers influence browser behavior. They do not prevent vulnerabilities in server-side code and do not replace authentication, authorization, input validation, context-aware output escaping, CSRF controls, dependency patching, safe file handling, TLS configuration, secret management, monitoring, or infrastructure security.
- CSP can break frontend assets and must be tailored and tested.
- CSP nonces cover inline elements that carry the nonce; they do not make untrusted inline content safe.
- HSTS preload is an operational commitment, not a checkbox.
- CORS is a browser access-control mechanism, not generic hardening, and belongs in Laravel's native configuration.
- Web servers, reverse proxies, and CDNs may add, remove, or overwrite headers outside Laravel.
X-XSS-Protectionis obsolete and is not emitted. Use output escaping and a carefully designed CSP as defense-in-depth.
Upgrading
Version 2 is a major redesign. The nine legacy middleware entry points are replaced by SecurityHeaders; wildcard CORS and X-XSS-Protection are removed. See UPGRADE.md for mappings and a migration checklist.
Contributing
See CONTRIBUTING.md for the development workflow and expectations.
Security Policy
Please report suspected package vulnerabilities privately as described in SECURITY.md. Do not disclose an unpatched vulnerability in a public issue.
License
Laravel Security is open-source software licensed under the MIT License.