Learn how to harden your Laravel application by configuring advanced security headers, implementing a strict CSP, and enforcing secure cookie flags.
Previously in this course, we explored custom middleware development to handle request interception and performance optimization. In this lesson, we build upon that foundation by focusing on the browser-server contract: security headers.
While Laravel provides sensible defaults, production-grade SaaS platforms require a more proactive stance against browser-based vulnerabilities like Cross-Site Scripting (XSS), clickjacking, and session hijacking. We will harden our application by implementing a strict Content Security Policy (CSP) and enforcing strict cookie security.
Security headers are instructions sent by your server to the client's browser, dictating how it should handle your site's content and cookies. They are the first line of defense against client-side attacks.
Cookie security is often overlooked until a session hijacking incident occurs. Every cookie in your application must explicitly define its scope and security constraints.
In your config/session.php and config/sanctum.php, ensure these flags are set:
secure: Ensures the cookie is only sent over HTTPS. Never set this to false in production.http_only: Prevents JavaScript from accessing the cookie via document.cookie, mitigating the impact of XSS.same_site: Controls cross-site request behavior. Set this to lax or strict to prevent CSRF.PHP#6A9955">// config/session.php 'secure' => env('SESSION_SECURE_COOKIE', true), 'http_only' => true, 'same_site' => 'lax', #6A9955">// Use 'strict' if your app doesn't rely on cross-site navigation
A Content Security Policy (CSP) tells the browser which sources of content (scripts, styles, images) are trusted. If an attacker manages to inject a malicious script, a properly configured CSP will block it from executing or reporting it to your telemetry endpoint.
Instead of writing raw headers, use a package like spatie/laravel-csp to define your policy in a fluent, object-oriented way.
Create a dedicated policy class in app/Policies/Csp/SaaSProductionPolicy.php:
PHPnamespace App\Policies\Csp; use Spatie\Csp\Policies\Policy; use Spatie\Csp\Directive; use Spatie\Csp\Keyword; class SaaSProductionPolicy extends Policy { public function configure() { $this->addDirective(Directive::BASE, Keyword::SELF) ->addDirective(Directive::CONNECT, [Keyword::SELF, 'https:#6A9955">//api.stripe.com']) ->addDirective(Directive::DEFAULT, Keyword::SELF) ->addDirective(Directive::SCRIPT, [Keyword::SELF, 'https:#6A9955">//js.stripe.com']) ->addDirective(Directive::STYLE, [Keyword::SELF, 'https:#6A9955">//fonts.googleapis.com']) ->addDirective(Directive::IMG, [Keyword::SELF, 'data:', 'https:#6A9955">//res.cloudinary.com']); } }
Register this policy in your AppServiceProvider or via the csp.php config file. This setup ensures that only scripts from your own domain and Stripe are allowed, neutralizing most third-party script injection vectors.
| Header | Purpose | Primary Threat Prevented |
|---|---|---|
Content-Security-Policy | Defines trusted content sources | XSS, Data Injection |
Strict-Transport-Security | Forces HTTPS connection | Man-in-the-Middle |
X-Content-Type-Options | Disables MIME-type sniffing | Drive-by downloads |
X-Frame-Options | Prevents framing | Clickjacking |
curl -I https://your-app.test to inspect your current headers. Note missing ones like Content-Security-Policy.Strict-Transport-Security header in your TrustProxies middleware to ensure browsers only connect via HTTPS for the next year.report-only mode first to ensure you don't break existing features.report-only neglect: Always deploy CSP in report-only mode first. Use a logging service or Sentry to monitor violations before switching to enforce.unsafe-inline: Many developers add unsafe-inline to their CSP to fix broken styles or scripts. This effectively disables the main benefit of CSP. Refactor your code to use nonce-based scripts instead.app.saas.com and marketing.saas.com), ensure your cookies are scoped correctly using the domain key in your session config, or risk session leakage.We have moved beyond basic Laravel defaults by:
secure, http_only, and same_site flags on all cookies.By hardening these headers, you ensure that your infrastructure is as resilient as your domain logic.
Up next: We will discuss Database Sharding Concepts and how to plan for data distribution as our SaaS platform scales.
Learn to build production-ready integrations by validating webhook signatures and offloading processing to queues to ensure security and system reliability.
Read moreMaster automated security testing by integrating static analysis and dependency auditing into your Laravel CI/CD pipeline to catch vulnerabilities early.
Security Header Configuration
Managing Third-Party API Integrations