Mailpit 1.30.3 catches everything your sites send. SMTP on 11025, web UI on 18025.
rex mail # list caught messagesrex mail open # open the web UIrex mail clear # delete ALL messagesFinding the one you want
Section titled “Finding the one you want”A busy local site fills Mailpit quickly, so the UI has an unread filter and Mark all read. Read state updates as you act on it rather than waiting for the next poll — a list that catches up seconds later is a list you stop trusting.
The catch-all is one switch, and it is on
Section titled “The catch-all is one switch, and it is on”Settings → Catch all outgoing mail. On, every site’s mail goes to Mailpit instead of the internet — even when a site is configured for a real SMTP provider. The app’s own words for the off position: “Turn it off only to test a live provider on purpose.” Off, a pool gets neither half of the catch, and a site’s mail leaves exactly as the site configured it.
Absent is on, deliberately: a site created before the switch existed is caught too, because “every site’s mail is caught” must not quietly mean “every site created after you found the switch”. Flipping it installs or removes a WordPress mu-plugin and restarts the running PHP pools, so the toggle stays pending until that is true of the machine.
An agent can flip it too, through the stack tool: catch_mail turns it on, and
stop_catching_mail is a system action, because in the tool’s own words it “lets every
site on this machine send REAL mail to real addresses.” Both need Changes on the
Agent access dial.
Three carriers, because each is a way an app escapes:
- PHP’s own
mail()— the pool’ssendmail_pathshim, which is what catches a plain WordPress site. - A WordPress SMTP plugin — WP Mail SMTP, FluentSMTP, Post SMTP and every plugin of that
shape hook
phpmailer_initand callisSMTP(), after which PHPMailer opens its own socket and the shim never sees the message. Measured on a real site:wp_mail()landed in Mailpit, the same call with the site callingisSMTP()went straight to the configured host. rexenv’s mu-plugin,rexenv-mail.php, hooks the same filter last and points PHPMailer at Mailpit. Install and removal are one operation with the switch. - Laravel —
sendmail_pathcannot reach it at all: Laravel’s sendmail transport does not consult php.ini, andMAIL_MAILER=sendmailplusMail::raw()exited 0, reported success and delivered nothing. The lever that works is Laravel’s own env precedence: a variable already in the process environment is never overwritten by.env. So the pool and the artisan runner carry nineMAIL_*keys — mailer, driver, URL, host, port, username, password, scheme, encryption — and they outrank your.env. Verified live:.envsaidMAIL_MAILER=log, and the message arrived in Mailpit.
The mu-plugin and the sendmail shim also reach a site on a FrankenPHP override, and the switch respawns that backend.
Other projects
Section titled “Other projects”Anything else that sends SMTP, point at:
host 127.0.0.1port 11025encryption noneauth noneFor a Laravel site rexenv serves, there is nothing to set — the environment above already
wins. For a Laravel app run from your own terminal, MAIL_HOST and MAIL_PORT in .env
with MAIL_MAILER=smtp.
Why not 1025 and 8025
Section titled “Why not 1025 and 8025”Those are Mailpit’s stock ports, and MailHog’s. Using them would mean rexenv collides with a standalone Mailpit, a MailHog, or Herd Pro’s bundled copy the moment both are running.
The offset is why you can keep whatever you already have. See Ports.
Reading a caught message
Section titled “Reading a caught message”rexenv has its own Mail screen: caught messages grouped by the site that sent them, and a
preview with four tabs — HTML, Text, Raw source and Headers. rex mail open
opens Mailpit’s own UI instead, which shows the same message plus its attachments. Between
them, the two questions local mail debugging is usually about: did it send at all, and does
the template survive a real mail client.
A link in the HTML preview
Section titled “A link in the HTML preview”An email is the one piece of HTML in the app that a stranger wrote, so the preview treats it
as one. Its HTML is rewritten before it is shown: a Content-Security-Policy meta is prepended
as the first thing in <head> — script-src 'none'; object-src 'none'; frame-src 'none'; base-uri 'none'; form-action 'none' — and then script, iframe, object, embed, base,
form and meta refresh are removed, along with every on* attribute and every
javascript:, vbscript: and data:text/html URL. Two layers, so an email’s scripts cannot
run even if the sweep misses the tag carrying it. Styles and images stay — that is what makes
an email look like the email.
Every click in the preview is cancelled first, link or not: the frame itself never navigates.
An http or https link opens in your preferred browser (the OS handler if you have not
chosen one). Anything else — mailto:, file:, a custom scheme — is named rather than
handed to the system opener:
That link isn’t a web address, so rexenv left it alone:
mailto:someone@example.test