My Laravel log file is publicly accessible. What should I do?
Laravel writes detailed error logs to storage/logs/laravel.log. When database connections fail or exceptions are thrown, those messages often include the full DSN — database host, username, and password — in plain text. If the file is reachable from the internet, those credentials are too.
First: block the path, then rotate every credential in the log.
The log is readable right now. Close the access first, then assume everything in it was read. Check the full file — logs grow; what leaked may be weeks of errors.
// the 60-second version
- Block
/storage/logs/in your web server config or move storage out of the web root. - Read the log carefully — rotate every credential you find.
- Fix the underlying error so credentials stop spilling into the log.
- Review access logs to see who downloaded the file.
01Why credentials end up in the log
Laravel catches unhandled exceptions and writes them with full context. When a database connection fails, the PDO exception message includes the DSN — which contains the host, port, username, and often the password:
[2025-03-14 08:22:11] local.ERROR: SQLSTATE[HY000] [2002] Connection refused
(Connection: mysql, SQL: ...)
PDOException with message 'SQLSTATE[HY000] [1045] Access denied
for user 'db_user'@'localhost' (using password: YES)'
"url": "mysql://db_user:p@ssw0rd@127.0.0.1:3306/myapp"
Other common sources of leaked secrets in logs:
- Mail configuration errors — SMTP password visible in connection exception.
- API call failures — request headers (with
Authorization: Bearer …) logged on HTTP error. - Queue/cache connection errors — Redis or SQS credentials in DSN.
- Verbose debug logging — stack traces that dump full request context including env values.
Laravel rotates log files daily by default, so laravel.log may only show today’s errors. Check for laravel-2025-03-13.log and similar date-stamped files in the same directory — all of them are potentially readable.
02Block the path immediately
Laravel’s storage/ directory should never be web-accessible. The correct fix is to make sure your web root points to the public/ subdirectory, not the project root:
root /var/www/myapp/public; # NOT /var/www/myapp
If you can’t change the document root right now, block the path explicitly:
location ~* ^/storage/ {
deny all;
return 404;
}
RedirectMatch 404 ^/storage/
Laravel ships with a public/.htaccess that blocks access to everything outside public/. If you’re on Apache and the web root is set correctly, this already protects you.
03Read the log and rotate every credential
Download the log and read it carefully before rotating anything — you need to know what was in it. Look for:
- Database DSN strings (contain username and password)
- SMTP credentials
- API keys or bearer tokens in request headers
- Redis/Memcache/SQS connection strings
- Any value from your
.envthat was logged in a stack trace
Rotate each one. For database passwords and API keys, change them at the source and update your .env. For tokens (OAuth, API keys), revoke the old ones and issue new ones.
04Stop credentials spilling into logs
After rotating, fix why they got there in the first place:
- Database connection failures — check your
DB_HOST,DB_PORT, firewall rules. A connection that keeps failing keeps logging. - Reduce exception verbosity in production — set
APP_DEBUG=falsein.env. With debug off, Laravel doesn’t include full stack traces with context in the log. - Use a log sanitizer — Laravel’s
$dontFlasharray on exception handlers can suppress sensitive request values from logs.
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
'token',
'api_key',
];
05Check who accessed the file
Review your web server access logs for requests to the log path:
grep -E "storage/logs/laravel" /var/log/nginx/access.log
A 200 response on a large file from an unknown IP is a strong indicator the log was downloaded. Note the IPs and check whether access correlates with any suspicious database activity or unusual API usage.
06Make sure it can’t happen again
- Always point the web root at
public/, not the project root. This is the most important protection. - Set
APP_DEBUG=falsein production — this reduces what ends up in the log. - Keep
storage/out of version control — addstorage/logs/to.gitignoreso logs don’t accidentally end up in repositories either. - Use a secrets manager — if credentials are injected at runtime via environment variables rather than stored in
.envfiles, a leaked log has far less impact.
Was this guide useful?
These playbooks are free to read and share. If a heads-up ever saved you a bad week, you can say thanks — or jump into the other guides.