Security 15 min read

Laravel Security Checklist 2026: 25 Checks Before You Ship to Production

A practical security checklist for Laravel developers. Covers .env protection, debug mode, session cookies, CORS, CSRF, security headers, dependency audits, and more. Copy this list into your deployment workflow.

Matt King
Matt King
April 15, 2026
Last updated: April 15, 2026
Laravel Security Checklist 2026: 25 Checks Before You Ship to Production

Every Laravel deployment has security decisions baked into it. Some are explicit (your CORS policy, your rate limiting rules). Most are implicit (whatever config/session.php shipped with). This checklist covers 25 things you should verify before every production deployment.

This is not theory. Every item on this list maps to a real vulnerability we have seen in production Laravel applications through StackShield's external scanning.


Environment & Configuration

1. APP_DEBUG is set to false

APP_DEBUG=false
APP_ENV=production

Debug mode exposes your entire environment to anyone who triggers an error. This includes database credentials, API keys, file paths, and your APP_KEY. Automated bots scan for Laravel debug pages constantly.

Verify: Visit a non-existent URL on your site. You should see your custom 404 page, not a Whoops/Ignition error page.

2. .env file is not publicly accessible

Your .env file should return 403 or 404, never 200. Test it:

curl -I https://yourdomain.com/.env

If you get a 200 response, your database credentials are public. See our fix guide for exposed .env files.

3. APP_KEY has been regenerated since initial setup

If you copied your APP_KEY from a tutorial, example repo, or colleague's machine, your encrypted data (sessions, cookies, passwords) can be decrypted by anyone with that key.

php artisan key:generate

4. composer audit passes with no critical advisories

composer audit

If any of your dependencies have known vulnerabilities, update them before deploying. See our complete guide to composer audit.

5. No development packages in production

Packages like laravel/telescope, spatie/laravel-ignition, and barryvdh/laravel-debugbar should be in require-dev, not require. If they must exist in production, gate them behind authentication.


Authentication & Sessions

6. Password hashing uses bcrypt or argon2

Laravel defaults to bcrypt, which is correct. Verify your config/hashing.php has not been changed to md5 or sha256. These are not suitable for password storage.

7. Login routes have rate limiting

Laravel Breeze and Jetstream include rate limiting by default. If you built custom authentication, verify you are using RateLimiter or the throttle middleware on login routes.

Route::post('/login', [AuthController::class, 'login'])
    ->middleware('throttle:5,1'); // 5 attempts per minute

8. Session cookies have Secure, HttpOnly, and SameSite flags

Check your config/session.php:

'secure' => env('SESSION_SECURE_COOKIE', true),
'http_only' => true,
'same_site' => 'lax',

Missing flags allow session hijacking over HTTP, JavaScript access to session cookies, and CSRF attacks. See our session security fix guide.

9. Session lifetime is reasonable

The default 120 minutes is fine for most apps. E-commerce checkout flows may need longer, but admin panels should use shorter lifetimes (15-30 minutes).


Input & Output

10. No unescaped user content in Blade templates

Use {{ $variable }} (escaped) by default. Only use {!! $variable !!} when you control the content and have sanitized it. Every {!! !!} in your templates is a potential XSS vector.

See our XSS prevention guide for safe patterns when you need to render HTML.

11. Form requests validate all input

Every controller action that accepts user input should use a Form Request class with explicit validation rules. Never trust $request->all() in a mass assignment.

12. File uploads validate MIME types and sizes

$request->validate([
    'avatar' => 'required|image|mimes:jpg,png,webp|max:2048',
]);

Never rely on file extension alone. Validate the actual MIME type.

13. CSRF protection is active on all state-changing routes

Every POST, PUT, PATCH, and DELETE form must include @csrf. API routes using token authentication can exclude CSRF, but session-based routes must not.

Verify that your VerifyCsrfToken middleware $except array is not overly permissive.


CORS & Headers

14. CORS policy does not use wildcard origins with credentials

Check your config/cors.php:

'allowed_origins' => ['https://yourdomain.com'],
'supports_credentials' => true,

Using 'allowed_origins' => ['*'] with 'supports_credentials' => true lets any website call your API as the logged-in user. See our CORS fix guide.

15. Security headers are configured

At minimum, set these headers (via middleware or web server config):

  • Strict-Transport-Security: max-age=31536000; includeSubDomains
  • X-Frame-Options: DENY or SAMEORIGIN
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin

Use our security headers checker to test your current configuration.

16. Content-Security-Policy is set (or planned)

CSP is the most effective defense against XSS. Start with report-only mode:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'

Infrastructure

17. SSL/TLS certificate is valid and auto-renewing

Check expiration and protocol versions:

openssl s_client -connect yourdomain.com:443 -tls1_2

Disable TLS 1.0 and 1.1. Support TLS 1.2 and 1.3 only.

18. Directory listing is disabled

curl -s https://yourdomain.com/storage/ | head -20

If you see a file listing, disable it. See our directory listing fix guide.

19. Document root points to /public

Your web server must serve from the /public directory, not the project root. If the document root is wrong, composer.json, .env, and your entire app/ directory are accessible.

20. Storage directory is not publicly browsable

/storage should not be accessible except through signed URLs or authenticated routes.

21. No database ports exposed to the internet

MySQL (3306), PostgreSQL (5432), and Redis (6379) should only be accessible from your application server, not the public internet.


Monitoring & Response

22. Error tracking is configured (not debug mode)

Use Sentry, Flare, or Bugsnag for error tracking in production. These tools capture errors without exposing them to users.

23. Telescope is gated behind authentication (if installed)

// app/Providers/TelescopeServiceProvider.php
protected function gate(): void
{
    Gate::define('viewTelescope', function ($user) {
        return in_array($user->email, ['admin@yourdomain.com']);
    });
}

24. Logging does not write sensitive data

Check that your log channel does not capture passwords, API keys, or PII. Review config/logging.php and any custom log calls.

25. External monitoring is active

The checks above catch issues at deployment time. But configuration can regress after server changes, hosting migrations, or infrastructure updates. Use StackShield to run these checks continuously and get alerted the moment something changes.


Use This Checklist

Copy this list into your deployment workflow. Run it before every release. Better yet, automate the external checks with StackShield's CI/CD integration so your pipeline fails if a critical security issue is detected.

Run a free scan to see how your app scores right now.

Frequently Asked Questions

What should be in a Laravel security checklist?

A complete Laravel security checklist covers environment configuration (APP_DEBUG=false, APP_ENV=production), secret management (.env file blocked, APP_KEY rotated), authentication (bcrypt/argon2, rate limiting on login), session security (Secure, HttpOnly, SameSite cookie flags), input validation (form requests, no {!! !!} without sanitization), CORS policy, CSRF protection, security headers (HSTS, CSP, X-Frame-Options), dependency audits (composer audit), and infrastructure checks (SSL, no directory listing, no exposed storage).

How often should I run a security checklist on my Laravel app?

Run the full checklist before every production deployment. Many issues (debug mode re-enabled, .env exposed after server migration, session config reverted) happen during deployments, not during normal operation. Automated tools like StackShield can run these checks continuously so you catch regressions immediately.

What is the most common Laravel security mistake?

Leaving APP_DEBUG=true in production. It exposes your entire .env file contents, database credentials, full stack traces, and application paths to anyone who triggers a 500 error. It is trivial to exploit and automated bots scan for it constantly.

Is Laravel secure by default?

Laravel provides strong security foundations: automatic CSRF protection, Blade output escaping, bcrypt password hashing, and encrypted cookies. But these defaults can be undermined by configuration mistakes (debug mode, wildcard CORS), developer choices (using {!! !!} for user content), and infrastructure issues (exposed .env, missing security headers). A checklist ensures the defaults are not accidentally weakened.

Related Security Terms

Stay Updated on Laravel Security

Get actionable security tips, vulnerability alerts, and best practices for Laravel apps.