Laravel File Upload Security: How to Validate Type, Size, and Storage Location

File uploads without type or size validation let attackers upload PHP shells, oversized files, or executable scripts that compromise your server.

High severity Application Security Updated 2026-05-01

The Problem

Unrestricted file uploads are a critical attack vector. Without proper validation, an attacker can upload a PHP file disguised as an image, access it via the web server, and execute arbitrary code on your server. Even if the file is not directly executable, oversized uploads can cause denial of service, and files with double extensions (image.php.jpg) can bypass naive extension checks. Laravel provides strong validation tools, but they must be applied explicitly.

How to Fix

  1. 1

    Validate file type using mimes and mimetypes rules

    Always validate the actual file type, not just the extension:

    $request->validate([
        'avatar' => 'required|file|mimes:jpg,jpeg,png,webp|max:2048',
        'document' => 'required|file|mimetypes:application/pdf|max:10240',
    ]);

    The mimes rule checks the file extension. The mimetypes rule checks the actual MIME type by reading the file content. Use both for maximum safety:

    'photo' => 'required|file|mimes:jpg,png|mimetypes:image/jpeg,image/png|max:5120',
  2. 2

    Set a maximum file size

    Always enforce a size limit to prevent denial of service:

    // In validation rules (size in kilobytes)
    'file' => 'required|file|max:10240', // 10 MB max
    // Also set PHP limits in php.ini
    upload_max_filesize = 10M
    post_max_size = 12M
    Set limits appropriate to your use case. Profile photos need 2 MB at most; documents might need 10 MB.
  3. 3

    Store uploads outside the public directory

    Never store uploads directly in public/. Use Laravel's storage system:

    // Store in storage/app/uploads (not publicly accessible)
    $path = $request->file('document')->store('uploads');
    // If you need public access, use the public disk with a unique name
    $path = $request->file('avatar')->store('avatars', 'public');
    // Generate a unique filename to prevent overwriting
    $path = $request->file('avatar')->storeAs(
        'avatars',
        Str::uuid() . '.' . $request->file('avatar')->extension(),
        'public'
    );
  4. 4

    Disable PHP execution in upload directories

    Even with validation, add a defense-in-depth layer by disabling PHP execution in upload directories.

    Nginx: location ~* /storage/.*\.php$ { deny all; return 404; }

    Apache (.htaccess in storage/app/public/):
    php_flag engine off
    <FilesMatch "\.php$">
        Order allow,deny
        Deny from all
    </FilesMatch>

How to Verify

Test by attempting to upload a PHP file:

# Create a test PHP file
echo '<?php phpinfo();' > test.php.jpg
# Try uploading via your form — it should be rejected

Also verify stored files are not directly executable by visiting their URL. Run php artisan stackshield:scan --check=SS009 to verify.

Prevention

Add file validation rules to every upload endpoint. Use a Form Request class for upload validation. Configure your web server to block PHP execution in storage directories. Consider using a CDN or object storage (S3) for user uploads to completely separate them from your application server.

Frequently Asked Questions

Can I trust the file extension to determine the file type?

No. An attacker can rename malicious.php to malicious.jpg. The mimetypes validation rule reads the file's actual content to determine its type, which is more reliable. Use both mimes (extension) and mimetypes (content) rules together.

Is it safe to store uploads in storage/app/public?

Yes, as long as you disable PHP execution in that directory and validate file types properly. Files in storage/app/public are served through the storage symlink (php artisan storage:link) and are accessible via URL, so they must be safe file types.

Related Security Terms

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