Laravel

A Laravel Security Checklist for Production

Most Laravel applications are secure by default until someone changes a default. The framework ships with sensible protections, but the way an app is configured, deployed and extended is where the real risk lives. This is the checklist I work through before I sign off on a Laravel application going to production. It is deliberately practical: every item is something I have seen go wrong on a real review, and every fix is a few lines of config or code.

Work through it top to bottom. If you want the reasoning behind each area in more depth, the Laravel Security Review hub links the deeper guides.

1. Lock down environment and configuration

Your .env file and app config decide how much an attacker learns when something breaks.

  • Set APP_DEBUG=false and APP_ENV=production. Debug mode renders full stack traces, environment variables and query contents to any visitor who triggers an error.
  • Set a real APP_KEY (php artisan key:generate) and never reuse it across environments. It encrypts sessions and cookies.
  • Keep .env out of version control and out of the web root. Confirm https://yoursite/.env returns 404, not the file.
  • Run php artisan config:cache so config is read from a compiled file, not re-parsed per request.

2. Force HTTPS and set security headers

Transport security is theory until it is enforced on every response.

// app/Providers/AppServiceProvider.php
public function boot(): void
{
    if ($this->app->environment('production')) {
        \URL::forceScheme('https');
    }
}

Add Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options and a Content-Security-Policy through middleware or your web server. A restrictive CSP is the single most effective defence against cross-site scripting.

3. Verify authentication and session settings

Authentication is where a small default becomes a large hole.

  • SESSION_SECURE_COOKIE=true and SESSION_HTTP_ONLY=true so session cookies never travel over HTTP or reach JavaScript.
  • Set SESSION_SAME_SITE=lax (or strict for sensitive apps).
  • Throttle login routes so credential stuffing is expensive. A single line of middleware does it: Route::post('/login', ...)->middleware('throttle:5,1');
  • Confirm passwords are hashed with bcrypt or argon2 (Laravel’s default) and never with md5 or sha1 from a legacy import.

The full treatment lives in Securing Authentication and Authorization in Laravel.

4. Guard the database layer

Eloquent parameterises queries for you, so SQL injection only reappears when you step outside it.

  • Never interpolate user input into DB::raw() or whereRaw(). Use bindings: whereRaw('votes = ?', [$count]).
  • Protect against mass assignment. Define $fillable explicitly rather than leaving a model wide open with $guarded = [].
  • Give the production database user only the privileges the app needs. It rarely needs DROP or GRANT.

5. Validate every input and escape every output

Validation is not a formality; it is your allow-list of what the application accepts.

$validated = $request->validate([
    'email' => 'required|email',
    'age'   => 'required|integer|min:0|max:150',
    'role'  => 'required|in:member,editor',
]);

On output, trust Blade’s {{ $value }} escaping and treat {!! $value !!} as a red flag to be justified case by case. For the reasoning behind tokens and request forgery, see Why do we use a token concept in PHP?.

6. Handle file uploads defensively

Uploads are the classic path from “user input” to “code execution.”

  • Validate with rules that check the real type: 'document' => 'required|file|mimes:pdf,docx|max:5120'.
  • Store uploads outside the web root, or on a dedicated disk, and never trust the original filename.
  • Serve user files through a controller that sets the content type, not by linking straight to the stored path.

The fundamentals carry over from file upload handling in PHP.

7. Audit dependencies before every release

Most breaches I investigate enter through an unpatched package, not custom code.

composer audit
composer outdated --direct

Pin versions, review the changelog of security releases, and update promptly. A vulnerable dependency two days after its patch is exactly how sites get compromised.

8. Control what errors and logs reveal

  • Confirm production returns a generic error page, never a stack trace.
  • Send logs somewhere durable, and scrub secrets and tokens before they are written.
  • Log authentication events (logins, failures, password changes) so you can reconstruct an incident.

Where to go next

This checklist is the spine of a review. Two companion guides go deeper on the areas that carry the most risk: authentication and authorization, and preventing the OWASP Top 10 in Laravel. For the framework’s built-in protections, start with 7 Laravel features to enhance the security of your application.

Related