← Blog

Validate an email address in Laravel

Laravel checks email syntax and MX out of the box — here's how to add the disposable and role-account checks it has no rule for.

Contents

You want to validate an email in Laravel, and you're wondering whether the framework already does enough. Mostly, it does. Laravel ships three email validators, and one of them goes further than a regex — it resolves the domain and checks for a mail server. This post covers what each rule actually does, where the built-in rules stop, and how to add the one check Laravel has no rule for: whether the address is disposable or a shared role inbox.

What Laravel validates out of the box

Laravel's email rule takes a comma-separated list of validators, backed by the egulias/email-validator package:

$request->validate([
    'email' => 'required|email:rfc,dns',
]);

The three you'll reach for:

  • email:rfc — RFC 5322 syntax. Structure only. bob@nowhere.invalid passes because it's shaped like an address.
  • email:filter — PHP's filter_var($value, FILTER_VALIDATE_EMAIL). Also syntax only, using PHP's own rules for the edge cases.
  • email:dns — this one does real work. It resolves the domain and confirms it has a mail (MX) record, so the address at least points at a domain that can receive mail. bob@nowhere.invalid fails here.

If you're wondering what that MX record is and why it matters, we wrote a short explainer: what is an MX record.

So email:rfc,dns gives you syntax plus a live MX check in one line. For a lot of forms, that is genuinely all you need — don't bolt an API call onto a signup form that a stdlib rule already covers.

What the built-in rules can't catch

email:dns tells you a domain can receive mail. It doesn't tell you what kind of address you've been handed. Two gaps matter for signups:

  • Disposable addresses. mailinator.com and thousands of throwaway domains have perfectly valid MX records. email:dns passes them.
  • Role accounts. info@, support@, sales@ — shared inboxes, not people. Valid syntax, valid MX, wrong thing to write into a "notify this user" column.

Laravel has no built-in rule for either. You'd be hand-maintaining a domain blocklist forever. That's the layer Boundstone's email endpoint covers.

Add a Boundstone check as a custom rule

POST /v1/verify/email returns the flags Laravel doesn't: disposable, role_account, and a free_provider flag, alongside valid_syntax, domain, and mx_found. Wrap it in a custom rule.

First, store your key:

// config/services.php
'boundstone' => [
    'key' => env('BOUNDSTONE_API_KEY'),
],

Then the rule (Laravel 10+ ValidationRule contract):

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Http;

class SignupEmail implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $response = Http::withToken(config('services.boundstone.key'))
            ->acceptJson()
            ->post('https://api.boundstone.io/v1/verify/email', [
                'email' => $value,
            ]);

        // Fail open: don't block a real user because a third party is down.
        if ($response->failed()) {
            return;
        }

        $result = $response->json();

        if ($result['disposable'] ?? false) {
            $fail('Use a permanent email address, not a disposable one.');
        }

        if ($result['role_account'] ?? false) {
            $fail('Use a personal address rather than a shared inbox like info@ or sales@.');
        }
    }
}

Http::withToken() sends the Authorization: Bearer bs_live_YOUR_KEY header for you. The fail-open branch is deliberate — an outage on our side should never lock a legitimate customer out of your signup form.

The rule is named SignupEmail, not DeliverableEmail, on purpose: it screens out disposable and role addresses, but it does not confirm the mailbox exists. Neither Laravel's MX check nor this call establishes deliverability, so the name shouldn't promise it.

Wire it into a FormRequest

Keep validation out of the controller. A FormRequest reads cleanly and puts the two layers side by side:

<?php

namespace App\Http\Requests;

use App\Rules\SignupEmail;
use Illuminate\Foundation\Http\FormRequest;

class StoreSubscriberRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'email' => [
                'required',
                'string',
                'email:rfc,dns',    // Laravel: syntax + MX
                new SignupEmail(),  // Boundstone: disposable + role
            ],
        ];
    }
}

Order matters: email:rfc,dns runs first and cheaply, so a malformed address never reaches the API call. If you'd rather run the check across several fields at once, withValidator() and $validator->after(...) give you the same result from a hook instead of a rule.

Read what was — and wasn't — checked

Every response carries a checks object. For a valid address it reads:

{
  "valid_syntax": true,
  "domain": "gmail.com",
  "mx_found": true,
  "disposable": false,
  "role_account": false,
  "free_provider": true,
  "checks": {
    "performed": ["syntax", "mx", "disposable_list", "role_list"],
    "not_performed": ["smtp_mailbox", "catch_all"]
  }
}

Read not_performed before you trust performed. Boundstone does not open an SMTP connection to the mailbox, and does not detect catch-all domains — smtp_mailbox and catch_all are listed as not performed, plainly, on every response. So disposable: false here means the domain isn't on a disposable list; it does not mean a human reads that inbox. We'd rather you know the boundary than sell you a "valid" that quietly guessed.

(free_provider is returned as information — gmail.com is a free provider — but it is not one of the performed checks, and it isn't a reason to reject anyone.)

The short version

  • Syntax only: email:rfc and email:filter. Don't ship these alone for signups.
  • Syntax + a real MX lookup: email:dns. Often all you need — use it before you reach for an API.
  • Disposable and role-account detection: no Laravel rule exists. That's Boundstone's /v1/verify/email, wrapped in a custom rule.
  • Trust the valid because of not_performed: no SMTP probe, no catch-all guess, and the response says so.

Paste an address into the free email validator to see the exact response first — no signup, no card. On plain PHP without Laravel? Same idea, different wiring: validate an email in PHP.

Frequently asked questions

How do I validate an email address in Laravel?

Laravel ships with a validation rule you attach to a form request or the Validator facade. The 'email' rule checks the address is syntactically well-formed, and 'email:dns' additionally confirms the domain publishes a valid MX or A record so it can receive mail. That covers formatting and domain deliverability, but it does not tell you whether the domain is a disposable throwaway or a role inbox, so many teams call an external verification API from the controller after the built-in rule passes.

Does Laravel's email validation confirm the mailbox actually exists?

No. Laravel's 'email' rule checks syntax, and with the 'dns' option it confirms the domain publishes mail records, but it never connects to the mail server to prove the individual mailbox is real. Confirming a specific mailbox would require SMTP-level probing, which reputable services (Boundstone included) deliberately do not perform because it is unreliable and can get your sending IP blocked. The honest limit is that a 'valid' result means the address is well-formed and the domain can accept mail, not that a human reads that inbox.

How do I block disposable or role-based email signups in Laravel?

Combine Laravel's built-in 'email' rule with an external verification call in your controller or form request. Boundstone's email endpoint returns syntax validity, whether the domain has a live MX record, whether it is on a disposable-provider list, whether it is a role account like info@ or support@, and whether it is a free provider, and every response lists exactly what it checked and what it did not. It plainly reports that SMTP mailbox verification and catch-all detection were not performed, so you know a 'valid' answer proves format, MX and list membership rather than that the mailbox will accept a message.

Thomas Tsui

Founder of Boundstone — building phone, email and IP validation you can actually verify.

One honest API for email, phone and IP — every response lists what it checked and what it didn't claim to. Free tier: 250 credits/month, no card, credits never expire.

More from Boundstone — API documentation · Benchmark methodology · The benchmark series · Buyer's checklist