laravel-mailpit-http maintained by manul-app
Laravel Mailpit HTTP transport
A Laravel mail transport that hands messages to Mailpit
over POST /api/v1/send instead of SMTP.
Why
Mailpit is usually reached on port 1025. That works on a laptop, and stops working the moment Mailpit moves to a shared staging box behind Cloudflare or any other reverse proxy: the web UI is happily served over HTTPS with Basic Auth, while port 1025 is not exposed at all.
Mailpit also accepts messages over its HTTP API, on the very same port as the UI. This package points Laravel's mailer at that endpoint, so a staging application can keep sending mail through the same hostname it already uses to read it.
Requirements
- PHP 8.3+
- Laravel 12 or 13
Installation
composer require manul-app/laravel-mailpit-http --dev
MAIL_MAILER=mailpit
MAILPIT_URL=https://mailpit-stage.example.com
MAILPIT_USERNAME=user
MAILPIT_PASSWORD=secret
That is the whole setup. The package is auto-discovered, registers the mailpit mailer
itself, and has nothing to publish — config/mail.php stays untouched.
Check it works
php artisan tinker
Mail::raw('Hello from the HTTP API', fn ($message) => $message->to('someone@example.com'));
The message shows up in the Mailpit UI right away. A misconfiguration throws instead of
failing silently — a wrong password reports HTTP 401 … check MAILPIT_USERNAME / MAILPIT_PASSWORD, and a missing MAILPIT_URL says so in as many words.
Configuration
| Variable | Required | Default | Meaning |
|---|---|---|---|
MAILPIT_URL |
yes | — | Mailpit base URL, without /api/v1/send. A trailing slash is fine, and so is a sub-path such as https://stage.example.com/mailpit |
MAILPIT_USERNAME |
no | null |
Basic Auth user. No Authorization header is sent when this is empty |
MAILPIT_PASSWORD |
no | null |
Basic Auth password |
MAILPIT_TIMEOUT |
no | 10 |
Request timeout, in seconds |
If you would rather spell the mailer out, a mailpit entry in config/mail.php takes
precedence over the environment:
'mailpit' => [
'transport' => 'mailpit',
'endpoint' => env('MAILPIT_URL'),
'username' => env('MAILPIT_USERNAME'),
'password' => env('MAILPIT_PASSWORD'),
'timeout' => (int) env('MAILPIT_TIMEOUT', 10),
],
The base URL lives under endpoint, not url: Laravel's MailManager reads a url
key in a mailer config as a DSN and would replace the transport name with the URL
scheme.
What gets sent
Sender, recipients, subject, both body parts, attachments and inline images are mapped onto the Mailpit send request. Two details are worth knowing:
- Bcc is sent as bare addresses, because that is the only shape Mailpit's API accepts for that field. Display names on Bcc recipients are dropped; every other recipient field keeps its name.
- Headers that Mailpit builds itself (
From,To,Subject,Date,Content-Type, and friends) are stripped from the payload — Mailpit rejects the whole message if you try to set them. Your ownX-*headers are passed through.
Scope
This is a one-way transport for local and staging environments. It does not read messages back out of Mailpit, and it is not meant for production: retries, queueing and failover are Laravel's job, not this package's.
Testing
composer test # Pest
composer lint # Pint
composer analyse # PHPStan
License
MIT. See LICENSE.
Unofficial, and not affiliated with the Mailpit project.