How to Fix an Exposed Laravel Storage Directory

Your Laravel storage directory is publicly accessible, exposing logs, cache files, and uploaded data. Learn how to restrict access.

High severity Application Security Updated 2026-03-01

The Problem

An exposed storage directory allows attackers to access your application logs (containing error details and potentially sensitive data), session files, cache data, and user-uploaded files that should be private. The storage directory contains framework-generated files and user uploads that were never intended to be publicly accessible. Automated scanners regularly check for exposed Laravel storage paths.

How to Fix

  1. 1

    Verify your document root

    The most common cause of an exposed storage directory is an incorrect document root. Your web server must point to /public, not the project root:

    {{ trim($paragraph)); ?>
    {{ trim($paragraph)); ?>

    With the correct document root, the storage directory is outside the web-accessible path and cannot be accessed directly.

  2. 2

    Remove or secure the storage symlink

    The php artisan storage:link command creates a symlink from public/storage to storage/app/public. Only files in storage/app/public should be web-accessible.

    Check what the symlink points to:

    {{ trim($paragraph)); ?>

    It should point to ../storage/app/public, not ../storage. If it points to the wrong location, recreate it:

    {{ trim($paragraph)); ?>

    Never store private files in storage/app/public. Use storage/app/private/ for sensitive uploads.

  3. 3

    Block direct access to storage paths

    Add web server rules to block access to storage directories that should not be public. In Nginx:

    {{ trim($paragraph)); ?>

    In Apache .htaccess:

    {{ trim($paragraph)); ?>

    This ensures only public/storage (the symlink to storage/app/public) is accessible.

  4. 4

    Serve private files through a controller

    For files that require authentication, serve them through a Laravel controller instead of direct file access:

    {{ trim($paragraph)); ?>

    if (!file_exists($fullPath)) { abort(404); }

    {{ trim($paragraph)); ?>
    {{ trim($paragraph)); ?>

How to Verify

Check that storage directories are not accessible:

curl -I https://yourdomain.com/storage/logs/laravel.log
curl -I https://yourdomain.com/storage/framework/sessions/
curl -I https://yourdomain.com/storage/app/

All should return 403 or 404. Only public/storage files (user-uploaded public assets) should return 200.

Prevention

Always use the correct document root. Store sensitive files in storage/app/private/ and serve through authenticated controllers. Never store user uploads in a publicly accessible directory without access controls. Use StackShield to monitor for exposed storage paths.

Frequently Asked Questions

What sensitive data is in the storage directory?

The storage directory contains: application logs with error details and stack traces (storage/logs/), session data for all users (storage/framework/sessions/), compiled Blade templates (storage/framework/views/), cached configuration (storage/framework/cache/), and uploaded files (storage/app/). Logs are especially dangerous as they may contain user data, API responses, and exception details.

Is the public/storage symlink safe?

The symlink from public/storage to storage/app/public is safe by design. Only files you explicitly place in storage/app/public are accessible. The risk comes from storing sensitive files in the public directory or having the wrong document root that exposes the entire storage directory.

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