Skip to content
208 changes: 208 additions & 0 deletions validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- [Error Message Indexes and Positions](#error-message-indexes-and-positions)
- [Validating Files](#validating-files)
- [Validating Passwords](#validating-passwords)
- [Validating Emails](#validating-emails)
- [Custom Validation Rules](#custom-validation-rules)
- [Using Rule Objects](#using-rule-objects)
- [Using Closures](#using-closures)
Expand Down Expand Up @@ -1240,6 +1241,8 @@ The `filter` validator, which uses PHP's `filter_var` function, ships with Larav
> [!WARNING]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to link this:

- The `filter` validator, which uses PHP's `filter_var` function, ships with Laravel and was Laravel's default email validation behavior prior to Laravel version 5.8.
+ The `filter` validator, which uses PHP's [`filter_var` function](https://www.php.net/manual/en/filter.constants.php#constant.filter-validate-email), ships with Laravel and was Laravel's default email validation behavior prior to Laravel version 5.8.
 
[!WARNING]

> The `dns` and `spoof` validators require the PHP `intl` extension.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could link this:

Suggested change
> The `dns` and `spoof` validators require the PHP `intl` extension.
> The `dns` and `spoof` validators require the [PHP `intl` extension](https://www.php.net/manual/de/book.intl.php).


Since email validation can be quite complex, you may use [Rule::email()](#validating-emails) to fluently construct the rule.

<a name="rule-ends-with"></a>
#### ends_with:_foo_,_bar_,...

Expand Down Expand Up @@ -2241,6 +2244,211 @@ Occasionally, you may want to attach additional validation rules to your default
// ...
});

<a name="validating-emails"></a>
## Validating Emails

Laravel provides a variety of validation rules that may be used to validate uploaded files, such as `rfc`, `strict` and `dns`. While you are free to specify these rules individually when validating emails, Laravel also offers a fluent email validation rule builder that you may find convenient:
To ensure that emails are valid according to your application's requirements, you may use `Rule` class to fluently define the rule via ` Rule::email()`:

```php
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

$validator = Validator::make($request->all(), [
'email' => ['required', Rule::email()],
]);
```
The `Email` rule object allows you to easily customize how emails are validated for your application, such as specifying that emails require RFC compliance, DNS checks, or spoof detection:

```php
// Basic RFC compliance...
Rule::email()->rfcCompliant();

// Strict RFC compliance...
Rule::email()->rfcCompliant(strict: true);

// Check for valid MX records...
Rule::email()->validateMxRecord();

// Prevent spoofing...
Rule::email()->preventSpoofing();
```
Of course, you may chain all the methods in the examples above:

```php
Rule::email()
->rfcCompliant(strict: true)
->validateMxRecord()
->preventSpoofing();
```

> [!WARNING]
> The `validateMxRecord()` and `preventSpoofing()` validators require the PHP `intl` extension.

<a name="defining-default-email-rules"></a>
#### Defining Default Email Rules
You may find it convenient to specify the default validation rules for emails in a single location of your application. You can easily accomplish this using the Email::defaults method, which accepts a closure. The closure given to the defaults method should return the default configuration of the `Email` rule. Typically, the `defaults` rule should be called within the boot method of one of your application's service providers:

```php
use Illuminate\Validation\Rules\Email;

/**
* Bootstrap any application services.
*/
public function boot(): void
{
Email::defaults(function () {
$rule = (new Email())->rfcCompliant(strict: true)->preventSpoofing(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be ; instead of , at the end


return $this->app->isProduction()
? $rule->validateMxRecord()
: $rule;
});
}
```
Then, when you would like to apply the default rules to a particular email undergoing validation, you may invoke the defaults method with no arguments:

```php
'email' => ['required', Email::defaults()],
```
Occasionally, you may want to attach additional validation rules to your default email validation rules. You may use the rules method to accomplish this:

```php
Email::defaults(function () {
return (new Email())->rfcCompliant(strict: true)
->preventSpoofing()
->rules(['ends_with:@example.com']);
});
```

<a name="email-validation-use-cases-examples"></a>
### Use Cases & Examples
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might alternatively be a table:

Method Description Use case example
rfcCompliant(strict: bool) Validates the email according to RFC 5322.
Passing true enforces stricter RFC checks (e.g., rejects invalid.@example.com).
Use rfcCompliant() if you need standard RFC validation while allowing some unusual formats.
Use rfcCompliant(true) if you want to reject less typical but technically valid addresses.
Basic RFC validation
Rule::email()->rfcCompliant();
Strict RFC validation
Rule::email()->rfcCompliant(true);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to add a link up there, where the string rule is explained:
#10112 (comment)


Each method on the `Email` rule addresses a different aspect of email validation.

<a name="rfc-compliant-email-validation"></a>
#### `rfcCompliant(strict: bool)`

**What it does**
- Validates the email according to [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322).
- Passing `true` enforces stricter RFC checks (e.g., rejects trailing dots or multiple consecutive dots).

**When to use it**
- Use `rfcCompliant()` for typical RFC validation while allowing some uncommon formats.
- Use `rfcCompliant(strict: true)` to reject unusual but technically valid addresses.

**Email addresses that pass on `rfcCompliant()` but fail on `rfcCompliant(strict: true)`**
- `test@example` (No TLD)
- `"has space"@example.com` (local part ends with a dot)

**Code sample**
```php
// Basic RFC validation
Rule::email()->rfcCompliant();

// Strict RFC validation
Rule::email()->rfcCompliant(strict: true);
```

<a name="validate-mx-record-email-validation"></a>
#### `validateMxRecord()`

**What it does**
- Ensures the domain has a valid MX record so it can receive mail.

**When to use it**
- Especially useful for sign-ups, password resets, or anywhere deliverability matters.

> [!NOTE]
> This rule triggers DNS lookups, which adds latency to the request and can fail if the domain’s DNS is temporarily unreachable.

**Code sample**
```php
Rule::email()->validateMxRecord();
```

<a name="prevent-spoofing-email-validation"></a>
#### `preventSpoofing()`

**What it does**
- Detects homograph or deceptive Unicode usage (e.g., Cyrillic “р” vs. Latin “r”) to prevent phishing or impersonation.

**When to use it**
- Ideal in security-sensitive contexts or where email spoofing is a concern.

**Code sample**
```php
Rule::email()->preventSpoofing();
```

> [!NOTE]
> The `validateMxRecord()` and `preventSpoofing()` methods require the PHP `intl` extension.

<a name="with-native-email-validation"></a>
#### `withNativeValidation()`

**What it does**
- Uses PHP’s `FILTER_VALIDATE_EMAIL` for validation.
- When `allowUnicode` is `true`, some Unicode characters are allowed.

**When to use it**
- If you want an additional layer of PHP’s built-in validation.
- Typically for ASCII addresses unless you explicitly allow Unicode.

**Email addresses that pass on `withNativeValidation()` but fail on `rfcCompliant(strict: true)`**
- `abc."test"@example.com`
- `name@[127.0.0.1]`
- `"ab\\(c"@example.com`

> [!NOTE]
> `withNativeValidation()` is **less strict** than RFC validation and was previously the default in Laravel. If you need strict RFC compliance, use `rfcCompliant(strict: true)` instead.

**Code sample**
```php
// Use native PHP validation (ASCII-only)
Rule::email()->withNativeValidation();

// Allow some Unicode
Rule::email()->withNativeValidation(allowUnicode: true);
```

<a name="email-validation-rule-matrix"></a>
### Email Validation Rule Matrix

Below is a single table showcasing different email address formats, highlighting whether each ✅ **passes** or ❌ **fails** the validation methods:

| **Email Address** | **rfcCompliant()** | **rfcCompliant(strict: true)** | **withNativeValidation()** | **withNativeValidation(allowUnicode: true)** | **validateMxRecord()** | **preventSpoofing()** |
|--------------------------------------|--------------------|--------------------------------|----------------------------|----------------------------------------------|------------------------|-----------------------|
| `"has space"@example.com` | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `abc."test"@example.com` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| `"ab\\(c"@example.com` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| `name@[127.0.0.1]` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| `test@example` <br>(no TLD) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| `test@localhost` <br>(no TLD) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| `plainaddress@example.com` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `tést@example.com` | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| `user@üñîçødé.com` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, user@üñîçødé.com is marked as failing withNativeValidation(allowUnicode: true), which seems counterintuitive—shouldn't it pass if Unicode is explicitly allowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, user@üñîçødé.com is marked as failing withNativeValidation(allowUnicode: true), which seems counterintuitive—shouldn't it pass if Unicode is explicitly allowed?

I would've expected it to, but it doesn't. These docs don't determine the behavior but describe it. The table shows why IMO rfcCompliant(strict: true) is a much better choice than withNativeValidation(allowUnicode: true) and I think withNativeValidation all together shouldn't be used, but thats an opinion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think withNativeValidation all together shouldn't be used, but thats an opinion

There's probably a reason, why this isn't the default anymore.

| `admin@examрle.com` <br>(Cyrillic р) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be helpful to clarify why admin@examрle.com (with Cyrillic р) passes rfcCompliant(strict: true) but fails preventSpoofing(). Since it’s a potentially deceptive address

Copy link
Contributor Author

@SanderMuller SanderMuller Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's a valid email address, just deceptive so potentially unwanted. So the RFC has nothing to do with how. I am not sure how to add these kind of details without making the docs for email validation a book on itself 😄

| `admin@exam<U+0440>le.com` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `test@@example.com` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |

<a name="combining-email-validation-methods"></a>
### Combining Methods

You can **chain** multiple methods to cover various scenarios:

```php
$validator = Validator::make($request->all(), [
'email' => [
'required',
Rule::email()
->rfcCompliant(strict: true) // Strict RFC validation
->validateMxRecord() // Requires valid MX record
->preventSpoofing() // Detects homograph attacks
],
]);
```

<a name="custom-validation-rules"></a>
## Custom Validation Rules

Expand Down