# Laravel Debug Mode in Production: What Attackers See

> 18% of Laravel apps run debug mode in production. Attackers use exposed stack traces, environment variables, and database credentials to compromise your app.

**Author:** Matt King | **Published:** March 9, 2026 | **Category:** Security

---

Laravel's debug mode is one of the most useful features during development. It gives you detailed stack traces, query logs, and environment information when something goes wrong. It is also one of the most dangerous things you can leave enabled in production.

This is not a theoretical risk. Debug mode exposures are one of the most common security findings in Laravel applications. Attackers actively scan for them because the information revealed makes further attacks trivially easy.

---

## What Debug Mode Exposes

When `APP_DEBUG=true` in production and an error occurs, Laravel's error handler (Ignition) displays a detailed debug page. Here is exactly what that page reveals:

| Exposed Information | Severity | Why It Matters |
|---------------------|----------|---------------|
| Full stack trace | High | Reveals internal file paths, class names, and application structure |
| Environment variables | Critical | Exposes DB_PASSWORD, APP_KEY, API keys, mail credentials, and every secret in your .env |
| Database connection details | Critical | Database host, port, username, and password shown in plain text |
| Server file paths | Medium | Reveals your deployment structure (/var/www/app, /home/forge, etc.) |
| Database queries | High | Shows raw SQL queries, including table names and column structures |
| PHP and Laravel version | Medium | Helps attackers target version-specific vulnerabilities |
| Loaded configuration | High | Full dump of all config values including third-party service credentials |
| Request data | Medium | Shows headers, cookies, session data, and input parameters |

That is not just an information leak. It is a complete blueprint of your application handed to anyone who triggers an error.

---

## How Attackers Exploit Debug Mode

Attackers do not just stumble on debug pages. They actively scan for them. Here is the typical attack flow:

### Step 1: Discovery
An attacker sends a request to a path that will trigger an error. Common techniques:

```
GET /non-existent-page HTTP/1.1
GET /%00 HTTP/1.1
GET /index.php/test HTTP/1.1
POST /api/v1/test with malformed JSON
```

If the response contains a stack trace instead of a generic error page, the application is in debug mode.

### Step 2: Credential harvesting
The debug page displays all environment variables. The attacker now has:

```
DB_HOST=production-db.internal.company.com
DB_DATABASE=app_production
DB_USERNAME=app_user
DB_PASSWORD=s3cur3_p@ssw0rd_2024

MAIL_USERNAME=apikey
MAIL_PASSWORD=SG.xxxxxxxxxxxxx

AWS_ACCESS_KEY_ID=AKIAxxxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxx

APP_KEY=base64:xxxxxxxxxxxxxxxx
```

### Step 3: Direct exploitation
With database credentials, the attacker connects directly to your database (if the port is open) or uses the AWS keys to access your cloud resources. The APP_KEY can be used to [decrypt encrypted data and forge session cookies](/blog/laravel-app-key-rce-exposure), giving the attacker full control over user sessions.

### Step 4: Ignition RCE (older versions)
In Laravel applications running Ignition versions before 2.5.2, the debug page itself has a remote code execution vulnerability ([CVE-2021-3129](https://nvd.nist.gov/vuln/detail/CVE-2021-3129)). An attacker can execute arbitrary PHP code through the debug page's "Run Solution" feature. This was actively exploited in the wild.

---

## How to Check If Debug Mode Is On

### Method 1: Check your .env file

```bash
# On your production server
grep APP_DEBUG /var/www/yourapp/.env
```

The output should be:

```
APP_DEBUG=false
```

If it says `APP_DEBUG=true`, your application is exposed.

### Method 2: Trigger a 404

Visit a URL that does not exist on your application:

```
https://yourapp.com/this-page-should-not-exist-12345
```

**If debug mode is ON:** You will see a detailed Ignition error page with a stack trace, environment details, and file paths.

**If debug mode is OFF:** You will see a generic "404 | Not Found" page or your custom error page.

### Method 3: Check response headers

Some debug tools add response headers. Look for:

```
X-Debug-Token: xxxxxx
X-Debug-Token-Link: /_profiler/xxxxxx
```

These indicate that Symfony's debug toolbar or profiler is active.

### Method 4: Check the config cache

If you are using config caching, the cached config might differ from your .env:

```bash
php artisan config:show app.debug
```

Or check the cached config directly:

```bash
grep "'debug'" bootstrap/cache/config.php
```

### Method 5: External monitoring

Use [StackShield](https://stackshield.io) to check from the outside. It probes your application for debug mode indicators the same way an attacker would, and alerts you if debug mode is detected.

---

## How to Fix It

### Step 1: Disable debug mode

In your production `.env` file:

```env
APP_DEBUG=false
APP_ENV=production
```

### Step 2: Clear and rebuild the config cache

```bash
php artisan config:clear
php artisan config:cache
```

This ensures the cached configuration reflects your current .env values.

### Step 3: Set a custom error handler for production

Create custom error pages so your users see something useful instead of a blank page:

```bash
mkdir -p resources/views/errors
```

Create `resources/views/errors/404.blade.php`:

```blade
@extends('layouts.app')

@section('content')
<div class="text-center py-20">
    <h1 class="text-4xl font-bold">Page Not Found</h1>
    <p class="mt-4 text-gray-600">The page you are looking for does not exist.</p>
    <a href="/" class="mt-6 inline-block text-blue-600">Go Home</a>
</div>
@endsection
```

Create `resources/views/errors/500.blade.php`:

```blade
@extends('layouts.app')

@section('content')
<div class="text-center py-20">
    <h1 class="text-4xl font-bold">Something Went Wrong</h1>
    <p class="mt-4 text-gray-600">We are working on fixing this. Please try again later.</p>
</div>
@endsection
```

### Step 4: Verify the fix

Visit a non-existent URL on your production site. You should see your custom error page, not a stack trace.

---

## How to Prevent It from Happening Again

Debug mode leaks usually happen during deployments. A developer sets `APP_DEBUG=true` to troubleshoot an issue, fixes the problem, and forgets to set it back. Or a staging .env file gets copied to production.

Here are concrete ways to prevent this:

### Use environment-specific .env files

Never copy .env files between environments. Each environment should have its own file managed separately. If you use Laravel Forge, Vapor, or Envoyer, environment variables are managed through their dashboards, not through .env files in your repository.

### Add a CI/CD check

Add a deployment step that verifies debug mode is off:

```yaml
# GitHub Actions example
- name: Verify debug mode is disabled
  run: |
    if grep -q "APP_DEBUG=true" .env.production; then
      echo "ERROR: Debug mode is enabled in production .env"
      exit 1
    fi
```

### Add a runtime check

Add a check in your `AppServiceProvider` that logs a warning if debug mode is on in production:

```php
// app/Providers/AppServiceProvider.php
public function boot()
{
    if (app()->environment('production') && config('app.debug')) {
        Log::critical('Debug mode is enabled in production!');

        // Optionally, force it off
        config(['app.debug' => false]);
    }
}
```

### Use config caching

Always run `php artisan config:cache` as part of your deployment process. This bakes your configuration into a cached file and ensures consistency. It also means accidental .env changes do not take effect until the cache is rebuilt.

### Set up continuous monitoring

Manual checks are unreliable. People forget. Deployments happen at 2 AM. A config cache rebuild pulls in the wrong value.

[StackShield](https://stackshield.io) monitors your application from the outside and checks for debug mode on every scan. If debug mode is ever detected in production, you get an alert in Slack, email, or via webhook within minutes, not when a customer reports seeing your database password.

---

## The Real Cost of Debug Mode in Production

Debug mode exposure is not just a security issue. It is a potential data breach.

If your debug page has been publicly accessible, you should assume your credentials have been compromised. That means:

1. **Rotate every secret** in your .env file: database passwords, API keys, mail credentials, APP_KEY
2. **Check access logs** for requests to error-triggering URLs
3. **Audit your database** for unauthorized access
4. **Review cloud provider logs** for API key usage
5. **Notify affected parties** if user data may have been accessed

This is exactly the kind of incident that continuous monitoring prevents. A $29/month monitoring tool is significantly cheaper than a breach response.

---

## Summary

Debug mode in production is one of the most common and most dangerous Laravel misconfigurations. It exposes everything an attacker needs to compromise your application, from database credentials to encryption keys.

**Fix it now:**
1. Set `APP_DEBUG=false` in your production .env
2. Run `php artisan config:cache`
3. Create custom error pages
4. Add CI/CD checks to prevent regressions
5. Set up [continuous monitoring](/features/continuous-monitoring) to catch it if it ever happens again

[Run a free scan](/free-scan) to check if your Laravel app has debug mode enabled, along with 30+ other security checks. Results in under 60 seconds.

---

## Frequently Asked Questions

### How do I check if debug mode is enabled on my Laravel application?

There are several ways to check. Visit a URL that does not exist (like /this-page-does-not-exist) and see if you get a detailed stack trace or a generic 404 page. You can also check your .env file for APP_DEBUG=true, or inspect the response headers for X-Debug-Token or similar debug indicators. StackShield checks for exposed debug mode automatically on every scan.

### What information does Laravel debug mode expose?

When APP_DEBUG is true, Laravel error pages display full stack traces, all environment variables (including database passwords, API keys, and mail credentials), file paths on your server, database query logs, loaded configuration values, and PHP/Laravel version information. This gives an attacker a complete map of your application internals.

### Can debug mode be exploited for remote code execution?

Yes. In older versions of Ignition (before 2.5.2), the debug page had a known remote code execution vulnerability (CVE-2021-3129). Even in patched versions, the information exposed by debug mode (file paths, environment variables, framework version) helps attackers craft targeted exploits against other vulnerabilities in your application.

### How do I show user-friendly error pages with debug mode off?

Laravel automatically shows generic error pages when APP_DEBUG is false. You can customize these by creating Blade templates in resources/views/errors/. For example, create 404.blade.php, 500.blade.php, and 503.blade.php with your own branded error pages. Laravel will use these automatically.

