thenetbotz / email-validator
Laravel package to validate emails using syntax, MX, disposable-domain and SMTP handshake checks.
v1.0.0
2026-06-02 19:58 UTC
Requires
- php: ^8.1
- ext-filter: *
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
A Laravel package that validates email addresses through four progressively stricter layers: syntax → disposable-domain check → MX lookup → SMTP RCPT-TO handshake (with catch-all detection).
No paid API. Works on PHP 8.1+ and Laravel 10/11/12.
Install
composer require thenetbotz/email-validator
Publish the config and disposable-domain list (optional):
php artisan vendor:publish --tag=email-validator-config php artisan vendor:publish --tag=email-validator-resources
Usage
Facade
use Thenetbotz\EmailValidator\Facades\EmailValidator; $result = EmailValidator::validate('jane.doe@example.com'); $result->valid; // bool $result->reason; // 'valid' | 'invalid_syntax' | 'no_mx_record' | 'disposable_domain' | 'smtp_rejected' | 'smtp_unreachable' | 'catch_all' $result->checks; // ['syntax' => true, 'mx' => true, 'disposable' => true, 'smtp' => true] $result->message; // server response or null
Dependency injection
use Thenetbotz\EmailValidator\EmailValidator; public function store(Request $r, EmailValidator $validator) { $result = $validator->validate($r->input('email')); }
As a validation rule
use Thenetbotz\EmailValidator\Rules\ValidEmail; $request->validate([ 'email' => ['required', new ValidEmail()], ]); // Reject catch-all domains too: $request->validate([ 'email' => ['required', new ValidEmail(allowCatchAll: false)], ]);
Batch CSV validation
php artisan email:validate emails.csv --out=results.csv php artisan email:validate emails.txt --skip-smtp
Configuration
config/email-validator.php:
'checks' => [ 'syntax' => true, 'mx' => true, 'disposable' => true, 'smtp' => true, ], 'smtp' => [ 'timeout' => 10, 'port' => 25, 'helo_host' => env('EMAIL_VALIDATOR_HELO', 'localhost'), 'from_email' => env('EMAIL_VALIDATOR_FROM', 'verify@example.com'), ], 'cache' => [ 'enabled' => true, 'ttl' => 86400, ],
Accuracy & speed
| Layers enabled | Accuracy | Speed (sequential) |
|---|---|---|
| Syntax only | ~50% | 100k+/sec |
| Syntax + MX | ~70% | ~5k/min |
| Syntax + MX + Disposable | ~75% | ~5k/min |
| All layers (incl. SMTP) | ~85–95% | ~500–2,000/hour |
Caveats:
- Many ISPs and cloud providers block outbound port 25. Run from a server with port 25 open.
- Gmail/Outlook/Yahoo intentionally accept any address to prevent enumeration — SMTP can't disprove existence on them.
- Catch-all domains accept everything; the package flags these via
REASON_CATCH_ALL. - Aggressive probing can get your IP blacklisted. Cache results (enabled by default).
Testing
composer install vendor/bin/phpunit
License
MIT