File Uploads in Filament Forms: Storage, Validation, and Security | FilaForms                                   [ ![Filaforms Logo](https://filaforms.app/logo.svg)FilaForms

 ](https://filaforms.app)   [ Features ](https://filaforms.app#features) [ Documentation ](https://docs.filaforms.app) [ Blog ](https://filaforms.app/blog) [ Pricing ](https://filaforms.app#pricing)   [ Try Demo ](https://filaforms.app/login) [ Get Started ](https://filaforms.app#pricing)

  [ Features ](https://filaforms.app#features) [ Documentation ](https://docs.filaforms.app) [ Blog ](https://filaforms.app/blog) [ Pricing ](https://filaforms.app#pricing) [ Try Demo ](https://filaforms.app/login) [ Get Started ](https://filaforms.app#pricing)

   ![FilaForms](https://filaforms.app/logo.svg) FilaForms

 TutorialsFile Uploads in Filament Forms: Storage, Validation, and Security
=================================================================

 filaforms.app/blog

  [    Back to blog ](https://filaforms.app/blog) [ Tutorials ](https://filaforms.app/blog/category/tutorials)

File Uploads in Filament Forms: Storage, Validation, and Security
=================================================================

 Manuk Minasyan ·  April 30, 2026  · 6 min read

 File uploads are where most form builders cut corners. They'll let you add a file field and call it done, leaving you to figure out storage and validation on your own. If you've ever had a user upload a `.php` file disguised as an image, you know why that's a problem.

I want to walk through how FilaForms handles file uploads, because I think the defaults here are worth understanding even if you never change them.

[\#](#adding-a-file-upload-field "Permalink")Adding a file upload field
-----------------------------------------------------------------------

In the FilaForms builder, you add a File Upload field the same way you'd add any other field type. Drop it onto your form, give it a field code, and you're done. If you've already [built a contact form](https://filaforms.app/blog/building-a-contact-form-in-laravel-with-filament-step-by-step) or [set up public-facing forms](https://filaforms.app/blog/how-to-add-public-facing-forms-to-your-filament-app), this will feel familiar.

Out of the box, each file upload field accepts a single file, caps uploads at 10 MB, and enables preview, download, and open. Original filenames are not preserved. Instead, files are renamed using ULID-based filenames for uniqueness. More on why that matters in the security section.

[\#](#storage-configuration "Permalink")Storage configuration
-------------------------------------------------------------

FilaForms uses a single config file to control where uploaded files land. In `config/filaforms.php`:

```
'storage' => [
    'disk' => env('FILA_FORMS_STORAGE_DISK', 'local'),
    'path' => 'form-submissions',
],

```

The default disk is `local`, which maps to `storage/app/private` in Laravel. Files stored here are not web-accessible. That's intentional. For most use cases (job applications, document submissions, anything with personal data), private storage is the right default.

If you need uploaded files to be publicly accessible (say, a photo gallery or user avatars), set the environment variable:

```
FILA_FORMS_STORAGE_DISK=public

```

Then run the standard Laravel command to create the symlink:

```
php artisan storage:link

```

That's it. No config changes beyond the env variable.

[\#](#how-files-are-organized-on-disk "Permalink")How files are organized on disk
---------------------------------------------------------------------------------

FilaForms doesn't dump everything into a flat directory. Uploaded files follow this structure:

```
form-submissions/{form-id}/{field-code}/{ulid}.{ext}

```

Each form gets its own directory. Within that, each file upload field gets its own subdirectory. So if you have a "Job Application" form with both a resume field and a cover letter field, those files live in separate directories.

This isolation matters for two reasons. First, cleanup is straightforward. Delete a form, and you know exactly which directory to remove. Second, access control becomes granular. You can set permissions per form or per field without worrying about collisions.

[\#](#validation-rules "Permalink")Validation rules
---------------------------------------------------

FilaForms gives you four validation options for file upload fields:

**file** is always applied automatically. It checks that what was uploaded is actually a valid file and not a malformed request.

**mimes** lets you restrict which file types are accepted. Set it to something like `pdf,doc,docx` for a document upload, or `jpg,png,webp` for images. This is server-side MIME validation, not just extension checking.

**max** sets the maximum file size in kilobytes. The global default is 10 MB (10240 KB), but you can lower this per field. A signature field probably doesn't need 10 MB. A portfolio upload might need more (up to whatever your PHP and server config allow).

**required** makes the file field mandatory. Without this, the field is optional by default.

Here's what a typical resume upload might look like in terms of validation:

- mimes: `pdf,doc,docx`
- max: `5120` (5 MB)
- required: yes

For a photo upload:

- mimes: `jpg,png,webp`
- max: `10240` (10 MB)
- required: no

[\#](#signature-fields "Permalink")Signature fields
---------------------------------------------------

FilaForms also has a Signature field, which is a canvas pad that captures handwritten signatures directly in the browser. It's worth mentioning here because it uses the same storage pipeline as file uploads.

The respondent signs on the canvas, and the signature is captured as a base64 data URI on the client side. On submission, FilaForms validates it, decodes the base64 data, and stores the result as an image file in the same directory structure as regular uploads.

Supported output formats are PNG, JPEG, and WebP. The max capture size is 512 KB. Signature fields are not searchable or sortable in the submissions table, which makes sense since they're images.

If you're building contracts or consent forms, this saves you from integrating a third-party signature service. Everything stays on your server.

[\#](#accessing-uploaded-files-in-code "Permalink")Accessing uploaded files in code
-----------------------------------------------------------------------------------

When you need to work with uploaded files programmatically, maybe to send them in a notification or include them in a CSV export, you have two options:

Using Laravel's Storage facade directly:

```
use Illuminate\Support\Facades\Storage;

$url = Storage::disk(config('filaforms.storage.disk'))->url($path);

```

Or using FilaForms' built-in StorageConfig helper:

```
$url = app(\FilaForms\Core\Support\StorageConfig::class)->url($path);

```

The StorageConfig approach is slightly better if you want to stay decoupled from the config key. If someone changes the storage config structure in a future version, the helper will still work.

[\#](#security-considerations "Permalink")Security considerations
-----------------------------------------------------------------

File uploads are one of the more dangerous things a web app can accept. FilaForms ships with defaults that handle the common attack vectors, and I think it's worth calling them out.

The `local` disk stores files in `storage/app/private`, which your web server doesn't serve. An attacker who guesses a file path still can't access it via URL. You have to explicitly opt into public storage.

Original filenames are stripped entirely. Every uploaded file gets a ULID-based name like `01HX7Z3KQGXYZ.pdf`. This prevents path traversal attacks (no `../../etc/passwd` in filenames) and makes file enumeration impractical. You can't guess the next filename because ULIDs include a random component.

The `mimes` rule checks actual file content on the server, not just the extension. Renaming `malware.php` to `malware.pdf` won't bypass it.

And as I covered earlier, each form and field combination has its own directory. A vulnerability in one form's file handling doesn't expose files from another form.

None of this replaces good server configuration. Make sure your web server doesn't execute files in the storage directory, and keep your PHP upload limits reasonable. But these defaults mean you're not starting from zero.

[\#](#common-patterns "Permalink")Common patterns
-------------------------------------------------

A few patterns I see come up frequently.

For resume or CV uploads, set mimes to `pdf,doc,docx`, max to 5 MB, and mark it required. Keep storage private (the default). Access these files through the admin panel or a custom controller that checks permissions.

For photo galleries or portfolios, set mimes to `jpg,png,webp` and increase max if needed. Use the public disk so images can be displayed on the frontend. Don't forget `php artisan storage:link`.

Document attachments like invoices or contracts are similar to resumes but often paired with a signature field. Keep both private and access them through authenticated routes only.

For support tickets or insurance claims, you'll probably want a mix of images and PDFs. Set mimes to `jpg,png,pdf`, keep storage private, and pair with [conditional logic](https://filaforms.app/blog/conditional-logic-in-laravel-forms-when-fields-should-show-and-hide) to show the upload field only when relevant.

That covers the full picture: storage, validation, directory layout, signatures, and the security defaults you get without writing any extra code. If you're running Filament and need file uploads on your forms, [FilaForms](https://filaforms.app) handles the plumbing so you don't have to.

 Related posts
-------------

 [  Tutorials   Apr 16, 2026

 Conditional Logic in Laravel Forms: When Fields Should Show (and Hide)
------------------------------------------------------------------------

You've built a form with ten fields, and half of them only matter sometimes. A "Company Name" field that nobody filling out a personal inquiry should ever see. A "Budget Range" dropdown that makes no sense until someone picks "Enterprise" from the plan selector. You know the drill.

 ](https://filaforms.app/blog/conditional-logic-in-laravel-forms-when-fields-should-show-and-hide) [  Tutorials   Apr 9, 2026

 Multi-Step Forms in Filament: A Complete Guide
------------------------------------------------

Long forms kill completion rates. You already know this. If you've ever put 15 fields on a single page and wondered why nobody finishes it, the answer is o

 ](https://filaforms.app/blog/multi-step-forms-in-filament-a-complete-guide) [  Tutorials   Apr 2, 2026

 Form Submission Tracking and Analytics in Laravel — Without Third-Party Tools
-------------------------------------------------------------------------------

You built a form. You embedded it on your site. People are submitting it. But how many people saw the form and didn't submit? How many started filling it o

 ](https://filaforms.app/blog/form-submission-tracking-and-analytics-in-laravel-without-third-party-tools)

    ![FilaForms Logo](/logo.svg) FilaForms

 Laravel form infrastructure for Filament. Stop rebuilding forms on every project.

 ### Product

 [ Features ](https://filaforms.app#features) [ Documentation ](https://docs.filaforms.app) [ Blog ](https://filaforms.app/blog) [ Pricing ](https://filaforms.app#pricing) [ Contact ](mailto:hello@filaforms.app)

 ### Legal

 [ Terms of Service ](https://filaforms.app/terms-of-service) [ Privacy Policy ](https://filaforms.app/privacy-policy)

  © 2025-2026 FilaForms. All rights reserved.

 [    ](mailto:hello@filaforms.app) [    ](https://x.com/MinasyanManuk)
