Skip to content

Feature/form request strict mode#59430

Merged
taylorotwell merged 6 commits into
laravel:13.xfrom
NurullahDemirel:feature/form-request-strict-mode
Apr 6, 2026
Merged

Feature/form request strict mode#59430
taylorotwell merged 6 commits into
laravel:13.xfrom
NurullahDemirel:feature/form-request-strict-mode

Conversation

@NurullahDemirel
Copy link
Copy Markdown
Contributor

@NurullahDemirel NurullahDemirel commented Mar 29, 2026

Summary

This PR introduces a global strict mode for FormRequest that rejects any input field not explicitly declared in the rules() method. The behavior can be toggled application-wide from AppServiceProvider, just like Model::shouldBeStrict() in Eloquent.

Motivation

Laravel's FormRequest validates the content of known fields but silently ignores fields that were never declared in rules(). In practice this means:

  • A client can send arbitrary extra fields (e.g. is_admin, role, balance) alongside a legitimate request, and they will pass validation without a single warning.
  • While $request->validated() correctly bounds its output to declared fields, raw request access via $request->all() or $request->input() — common in middleware, service classes, or when the request object is passed directly — allows undeclared fields to flow through silently.
  • Mass-assignment style bugs are not limited to Eloquent; the same class of error can occur anywhere validated input is forwarded — to jobs, actions, service classes, or third-party APIs.

Usage

Option 1 — Global, via AppServiceProvider:

<?php

namespace App\Providers;

+use Illuminate\Foundation\Http\FormRequest;
 use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        //
    }

    public function boot(): void
    {
+        FormRequest::failOnUnknownFields(! app()->isProduction());
    }
}

Option 2 — Per-class override:

class PublicWebhookRequest extends FormRequest
{
    // Opt this specific request out of the global strict mode
    protected ?bool $failOnUnknownFields = false;

    public function rules(): array { ... }
}

Backward Compatibility

Fully backward compatible. The flag defaults to false, meaning no existing behavior changes unless the developer explicitly calls FormRequest::failOnUnknownFields() or sets $failOnUnknownFields on a specific request class.

@yasser942
Copy link
Copy Markdown

very useful

@eticaretteyimx
Copy link
Copy Markdown

Very Very Good

@browner12
Copy link
Copy Markdown
Contributor

Can you double check your statement about $request->validated() being unbounded? When I dump this I only get values that have been validated in my form request.

@NurullahDemirel
Copy link
Copy Markdown
Contributor Author

Can you double check your statement about $request->validated() being unbounded? When I dump this I only get values that have been validated in my form request.

You're absolutely right — $request->validated() is already bounded to declared fields, that bullet point was inaccurate. I've updated the description accordingly.
The real motivation here is broader: undeclared fields pass through the validation layer silently with no warning, which means they can flow freely via $request->all() or $request->input() through middleware, service classes, or anywhere the request object is passed directly. This feature makes the contract between client and server explicit at the FormRequest level — the same philosophy Model::shouldBeStrict() brings to Eloquent.

@JurianArie
Copy link
Copy Markdown
Contributor

This would be really helpful, especially consuming APIs in a development environment. I make more typos than I’d like to admit when integrating with APIs, and having unknown fields fail fast would save a lot of debugging time.

@NurullahDemirel
Copy link
Copy Markdown
Contributor Author

This would be really helpful, especially consuming APIs in a development environment. I make more typos than I’d like to admit when integrating with APIs, and having unknown fields fail fast would save a lot of debugging time.

That's exactly the use case this was designed for! The ! app()->isProduction() pattern in the usage example is intentional for this reason — strict in development so typos like emial instead of email fail immediately rather than silently passing through and causing confusing behavior downstream, while staying lenient in production to avoid breaking live traffic. Glad it resonates.

@shaedrich
Copy link
Copy Markdown
Contributor

Option 2 — Per-class override:

class PublicWebhookRequest extends FormRequest
{
    // Opt this specific request out of the global strict mode
    protected ?bool $failOnUnknownFields = false;

    public function rules(): array { ... }
}

Have you considered also adding an attribute?

// Opt this specific request out of the global strict mode
#[FailOnUnknownFields]
class PublicWebhookRequest extends FormRequest
{
    public function rules(): array { ... }
}

@NurullahDemirel
Copy link
Copy Markdown
Contributor Author

Option 2 — Per-class override:

class PublicWebhookRequest extends FormRequest
{
    // Opt this specific request out of the global strict mode
    protected ?bool $failOnUnknownFields = false;

    public function rules(): array { ... }
}

Have you considered also adding an attribute?

// Opt this specific request out of the global strict mode
#[FailOnUnknownFields]
class PublicWebhookRequest extends FormRequest
{
    public function rules(): array { ... }
}

Why not? That sounds good. Thank you for your feedback.

@taylorotwell taylorotwell merged commit 9f92a15 into laravel:13.x Apr 6, 2026
51 of 52 checks passed
jonagoldman pushed a commit to deplox/laravel-framework that referenced this pull request Apr 30, 2026
* strict mode for validation

* delete validation.php file

* formated

* formatting

* formatting

* more tests

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants