What Is Security Headers?
HTTP response headers that instruct browsers how to handle your website's content securely. They protect against common attacks like cross-site scripting (XSS), clickjacking, and protocol downgrade attacks by telling the browser what actions are allowed.
How It Works
Security headers work by extending the HTTP response with browser-side security policies. The browser reads and enforces these policies before executing any page content.
Content-Security-Policy (CSP) — When the browser receives a response containing Content-Security-Policy: default-src 'self'; script-src 'self' cdn.yourapp.com, it builds a whitelist of permitted resource origins before parsing the HTML. When the parser encounters a <script> tag, an inline event handler, or a resource load, it checks the source against the policy. Anything not matching the whitelist is silently blocked and reported to the browser console. An XSS payload injected into the page (<script>document.location='https://evil.com'</script>) fails because inline scripts are not whitelisted.
X-Frame-Options: DENY — When the browser attempts to render a page inside an <iframe>, it checks this header first. DENY causes the browser to refuse rendering in any frame context. This prevents clickjacking attacks where an attacker embeds your page invisibly in an iframe and tricks users into clicking elements they believe are part of the attacker's page.
Strict-Transport-Security — On first receipt, the browser stores the HSTS policy and for all future requests to your domain converts HTTP to HTTPS before the network request is sent. Prevents SSL stripping attacks that target the first HTTP request.
X-Content-Type-Options: nosniff — Prevents the browser from MIME-sniffing a response away from its declared Content-Type. Without this header, a browser might execute a .jpg file as JavaScript if it detects script-like content — enabling attacks where an attacker uploads a file with a safe extension but executable content.
Referrer-Policy — Controls how much referrer information the browser includes in the Referer header when navigating away from your page. strict-origin-when-cross-origin sends the origin for cross-origin requests but the full URL for same-origin requests, preventing leaking sensitive URL parameters (authentication tokens, user IDs) to third-party analytics or ad networks.
Types of Security Headers
Missing CSP — XSS Execution
Without a Content-Security-Policy, any XSS payload injected into a page executes unconditionally — the browser has no policy to enforce that would block inline scripts or scripts from attacker-controlled origins.
# Check if CSP is present
curl -sI https://yourapp.com | grep -i "content-security-policy"
# Empty output means XSS payloads execute unrestricted
Missing X-Frame-Options — Clickjacking
Without X-Frame-Options or a CSP `frame-ancestors` directive, any site can embed your application in an invisible iframe and use UI tricks to make users perform unintended actions.
<!-- Attacker page: invisible iframe overlaying a "Win a prize" button -->
<iframe src="https://yourapp.com/account/delete"
style="opacity:0; position:absolute; top:0; left:0;
width:100%; height:100%; z-index:9999;">
</iframe>
Missing HSTS — Protocol Downgrade
Without Strict-Transport-Security, the initial HTTP request to your site can be intercepted and SSL-stripped by a network-level attacker, downgrading the connection to plaintext.
# Check if HSTS header is present and has adequate max-age
curl -sI https://yourapp.com | grep -i "strict-transport"
# Should show: max-age=31536000; includeSubDomains
Missing X-Content-Type-Options — MIME Sniffing
Without `X-Content-Type-Options: nosniff`, browsers may interpret files with incorrect MIME types according to their detected content, potentially executing attacker-uploaded files as scripts.
# Uploaded file: shell.jpg (actually PHP or JavaScript content)
# Without nosniff, browser may execute based on content detection
In Laravel Applications
Laravel does not set security headers by default. You need to add them via middleware. Key headers include Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.
Code Examples
SecurityHeaders Middleware: Before and After
Vulnerable
// No security headers middleware in app/Http/Kernel.php
// curl -I https://yourapp.com returns:
// HTTP/2 200
// content-type: text/html; charset=UTF-8
// set-cookie: XSRF-TOKEN=...
// (No X-Frame-Options, no CSP, no HSTS, no X-Content-Type-Options)
Secure
// app/Http/Middleware/SecurityHeaders.php
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', 'DENY');
$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=()');
if (app()->environment('production')) {
$response->headers->set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains'
);
}
return $response;
}
}
// Register in app/Http/Kernel.php:
// protected $middleware = [
// \App\Http\Middleware\SecurityHeaders::class,
// // ... other middleware
// ];
Real-World Example
Adding `X-Frame-Options: DENY` prevents your Laravel application from being embedded in an iframe, blocking clickjacking attacks.
Why It Matters
Security headers are one of the highest-value, lowest-effort security improvements available to Laravel developers. Adding six HTTP response headers takes an hour but prevents entire classes of attacks: XSS via Content-Security-Policy, clickjacking via X-Frame-Options, MIME sniffing via X-Content-Type-Options, and protocol downgrade via Strict-Transport-Security.
Laravel does not add security headers by default. You must implement them via middleware. The most maintainable approach is a dedicated SecurityHeaders middleware registered in the $middleware array in app/Http/Kernel.php. Alternatively, the spatie/laravel-csp package provides a configuration-driven approach to Content-Security-Policy.
Content-Security-Policy (CSP) is the most powerful but also most complex security header. It specifies which sources of scripts, styles, images, and other resources are permitted. A well-configured CSP prevents XSS from executing even if an injection vulnerability exists. However, an incorrectly configured CSP can break your application by blocking legitimate resources.
Checking your current headers takes seconds: curl -I https://yourdomain.com shows all response headers. Tools like securityheaders.com grade your headers and show exactly what is missing and what each header does.
Common Misconceptions
Myth: Security headers are optional extras — the real security is in the application code.
Reality: Security headers are a critical defense-in-depth layer. A Content-Security-Policy header can prevent XSS exploitation even when an injection vulnerability exists in your code. X-Frame-Options prevents clickjacking regardless of your application logic.
Myth: Setting `Content-Security-Policy: default-src 'self'` is always safe.
Reality: An overly restrictive CSP will break your application if you load any external resources (Google Fonts, CDN-hosted scripts, analytics). CSP must be configured to match your actual resource requirements.
Myth: HTTPS makes security headers unnecessary.
Reality: HTTPS encrypts data in transit but does nothing to prevent XSS, clickjacking, or MIME sniffing. Security headers and HTTPS solve different problems and both are required.
How to Detect This
Run `curl -I https://yourdomain.com` and check the response headers for `Content-Security-Policy`, `Strict-Transport-Security`, `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, and `Permissions-Policy`. Any of these missing is a finding. Use Mozilla Observatory (observatory.mozilla.org) for a graded report with remediation guidance. StackShield checks security headers on every scan and alerts you when previously-present headers disappear — which can happen after a web server update, a deployment configuration change, or a CDN misconfiguration. In Laravel, verify your security headers middleware is registered in `app/Http/Kernel.php` by checking the `$middleware` array.
How to Prevent This in Laravel
-
1
Create a `SecurityHeaders` middleware class and register it in `app/Http/Kernel.php` in the global `$middleware` array so it applies to every response.
-
2
Set `X-Frame-Options: DENY` unless your application intentionally supports embedding in iframes — if it does, use `SAMEORIGIN` to restrict embedding to your own domain only.
-
3
Add `Strict-Transport-Security: max-age=31536000; includeSubDomains` only in production (gate on `app()->environment('production')`), since HSTS on a development server that uses HTTP can lock developers out.
-
4
Install `spatie/laravel-csp` via Composer for a configuration-driven Content-Security-Policy — use report-only mode first (`Content-Security-Policy-Report-Only`) to audit violations before switching to enforcement.
-
5
Run `curl -sI https://yourapp.com | grep -E "Content-Security|X-Frame|Strict-Transport|X-Content-Type|Referrer"` after every deployment to verify all five critical headers are present.
-
6
Check your headers score at securityheaders.com and Mozilla Observatory (observatory.mozilla.org) — both provide graded reports with specific remediation guidance for missing or misconfigured headers.
Frequently Asked Questions
How do I add security headers in Laravel?
Create a middleware class with `php artisan make:middleware SecurityHeaders`, implement the `handle()` method to call `$response->headers->set()` for each header, and register the middleware in the global `$middleware` array in `app/Http/Kernel.php`. Alternatively, install `spatie/laravel-csp` for a configuration-driven approach to Content-Security-Policy, which is the most complex header to get right.
Will a strict Content-Security-Policy break my application?
A poorly configured CSP can break your application if it blocks legitimate resources. Start with `Content-Security-Policy-Report-Only` mode which enforces nothing but logs violations to the browser console and a report endpoint. Review violations for a week to understand which sources your application genuinely needs, then build an enforcement policy that allows those sources. Common breaks include inline scripts (use nonces), Google Fonts (add `fonts.googleapis.com`), and CDN-hosted libraries.
Do security headers affect application performance?
Security headers add a negligible amount to response size (typically under 500 bytes total) and have no measurable impact on application performance. The browser enforcement of CSP does add a tiny parsing step, but modern browsers perform this in microseconds. The only operational consideration is that a CSP violation can break functionality for users if the policy is too restrictive — which is why report-only mode during configuration is important.
Should I use spatie/laravel-csp or write my own security headers middleware?
For Content-Security-Policy specifically, `spatie/laravel-csp` is the better choice — it provides a fluent API for building policies, nonce generation for inline scripts, and structured policy classes that are easier to maintain than string concatenation. For the other headers (X-Frame-Options, HSTS, X-Content-Type-Options, Referrer-Policy), a simple custom middleware is sufficient and has no external dependencies. Many teams use `spatie/laravel-csp` for CSP plus a custom middleware for the remaining headers.
Related Terms
Cross-Site Scripting (XSS)
A vulnerability where an attacker injects malicious JavaScript into web pages viewed by other users. The injected script runs in the victim's browser with the same privileges as legitimate scripts, allowing the attacker to steal session tokens, redirect users, or modify page content.
Cross-Site Request Forgery (CSRF)
An attack where a malicious website tricks a user's browser into performing an unwanted action on a site where the user is authenticated. The browser automatically sends cookies with the request, so the target site processes it as a legitimate action from the user.
HTTP Strict Transport Security (HSTS)
A security header that tells browsers to only connect to your website over HTTPS. Once a browser receives the HSTS header, it will automatically convert all future HTTP requests to HTTPS for the specified duration, preventing protocol downgrade attacks and cookie hijacking.
Related Articles
95% of Cloud Security Failures Are Misconfigurations: A Laravel Deployment Checklist
95% of cloud security failures stem from human error, not platform vulnerabilities. This checklist covers the most common misconfigurations in Laravel deployments: exposed storage buckets, overly permissive IAM, missing network segmentation, database exposure, Redis security, leaked .env files, and debug endpoints left open in production.
ISO 27001 for Laravel Applications: Controls, Annex A, and What Developers Must Implement
ISO 27001:2022 defines 93 Annex A controls across four domains. This guide maps the technological controls that directly affect Laravel developers to specific implementations: access control, authentication, logging, cryptography, secure development, and continuous monitoring.
Securing Your Laravel CI/CD Pipeline: A Practical DevSecOps Guide
With 70% of teams releasing continuously, your CI/CD pipeline is a high-value target. This guide covers securing GitHub Actions and GitLab CI for Laravel projects: secrets management, composer audit integration, SAST scanning, container security, deployment hardening, and artifact signing with practical YAML configs.
Related Fix Guides
Monitor Your Laravel Application's Security
StackShield continuously checks your Laravel application from the outside, catching security issues before attackers find them.
Start Free Trial