Laravel Debug Routes in Production: How to Find and Remove Test Endpoints
Debug and test routes left in production expose phpinfo(), database dumps, and internal application state to anyone who finds them.
The Problem
Developers often create temporary routes during development for testing — routes that dump phpinfo(), display database contents, test email sending, or expose internal state. If these routes reach production, they become an information disclosure vulnerability. Attackers routinely scan for common debug paths like /test, /debug, /phpinfo, /info, and /_dev. A single phpinfo() route reveals your PHP version, extensions, environment variables, and server configuration.
How to Fix
-
1
Find debug routes in your route files
Search for common debug patterns:
grep -rn 'phpinfo\|dd(\|dump(\|var_dump\|print_r' routes/ --include='*.php' grep -rn 'test\|debug\|tmp\|temp\|_dev' routes/ --include='*.php'Also list all registered routes and look for suspicious ones:
php artisan route:list | grep -i 'test\|debug\|info\|dump' -
2
Remove or protect debug routes
Delete debug routes entirely, or if they are needed for development, wrap them in an environment check:
// REMOVE completely — preferred // Route::get('/phpinfo', fn() => phpinfo());// Or restrict to local environment only if (app()->environment('local')) { Route::get('/debug-info', function () { return response()->json([ 'php' => phpversion(), 'laravel' => app()->version(), ]); }); } -
3
Add a CI check to prevent debug routes in production
Add a test that fails if debug routes exist:// tests/Feature/NoDebugRoutesTest.php public function test_no_debug_routes_exist(): void { $debugPatterns = ['phpinfo', 'debug', '_test', '_dev']; $routes = collect(Route::getRoutes())->map(fn ($r) => $r->uri());foreach ($debugPatterns as $pattern) { $matches = $routes->filter(fn ($uri) => str_contains($uri, $pattern)); $this->assertEmpty($matches, "Debug route found: {$matches->implode(', ')}"); } }
How to Verify
Check common debug paths return 404:
curl -s -o /dev/null -w '%{http_code}' https://yourapp.com/phpinfo
curl -s -o /dev/null -w '%{http_code}' https://yourapp.com/test
curl -s -o /dev/null -w '%{http_code}' https://yourapp.com/debug
All should return 404. Run php artisan stackshield:scan --check=SS051 to verify.
Prevention
Never create debug routes in main route files. Use php artisan tinker for ad-hoc debugging. Add the CI test above to catch debug routes before deployment. Use StackShield to scan for debug routes continuously.
Frequently Asked Questions
Is it safe to use dd() in controllers?
dd() (dump and die) should never exist in production code. It halts execution and outputs internal data to the browser. Use proper logging (Log::debug()) instead, and remove dd() calls before committing.
What about Laravel Telescope routes?
Telescope registers routes at /telescope by default. In production, either disable Telescope entirely (TELESCOPE_ENABLED=false) or restrict access using the gate in TelescopeServiceProvider. Stackshield check SS013 specifically monitors for this.
Related Security Terms
Related Guides
Laravel Debug Mode in Production: How to Disable APP_DEBUG and Stop Leaking Secrets
APP_DEBUG=true in production exposes stack traces, environment variables, and database credentials to anyone who triggers an error. Here is how to disable it safely and verify the fix.
How to Fix an Exposed Laravel Telescope Dashboard
Your Laravel Telescope dashboard is publicly accessible in production, exposing requests, queries, and application data. Secure it now.
How to Fix Exposed Laravel Ignition Error Pages
Laravel Ignition error pages are visible in production, leaking stack traces and environment details. Learn how to disable them.
Detect This Automatically with StackShield
StackShield continuously monitors your Laravel application from the outside and alerts you when security issues are found. No installation required.
Start Free Trial