What’s New in Mailbox for Laravel 3.0: Safer Defaults and a Faster Dashboard

Nika Jorjoliani

Head of Engineering

We built Mailbox for Laravel to answer a question every Laravel team runs into: what does this email actually look like before it reaches a real inbox? It captures your app’s outgoing mail and shows it in a dashboard inside the app, with no external service and no extra process. Since we open-sourced it, Mailbox has been installed more than 35,000 times from Packagist, and teams now run it well beyond their own laptops: on shared staging servers, review apps and CI.

Mailbox 3.0, released on October 6, is built for that wider use. Its focus is security and stability. Captured email stays private by default, previews are sanitized more thoroughly, the dashboard does far less work on every refresh, and attachments, search, and delivery now behave correctly in the edge cases. Here is what changed, how it works under the hood, and how to upgrade.

What’s new in Mailbox for Laravel 3.0?

  • Private by default. The dashboard opens in your local environment out of the box. Staging and other shared environments get access through a gate you define.
  • Stronger email previews. Captured HTML is now sanitized on a parsed DOM tree instead of with pattern matching.
  • A faster dashboard. A full page of attachments loads with one query, however many messages are on screen.
  • More reliable capture. Thirteen fixes across attachments, search, message deduplication and delivery.
  • Laravel 11, 12 and 13, with CI now running PHP 8.5 for Laravel 12 and 13.

How does Mailbox 3.0 keep captured email private?

The dashboard is now local-only by default, and every other environment needs a gate you write yourself.

Captured emails often hold the most sensitive links an application sends: password resets, magic sign-in links, invitations. On a developer’s machine that is fine. On a staging server with a public URL, it needs an explicit decision about who can look.

In 2.x, the built-in viewMailbox gate allowed a request when the app was local or when mailbox.enabled was true, and mailbox.enabled defaults to true everywhere except production. That made the dashboard open on any environment where it was installed. In 3.0 the default gate allows APP_ENV=local and nothing else. Anywhere else, requests get a 403, or a redirect tomailbox.unauthorized_redirect, until you grant access:

use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('viewMailbox', fn ($user) => $user?->isAdmin());
}

The redirect itself is smarter too. unauthorized_redirect now applies only to guests, so a signed-in user without access gets a clear 403 instead of being sent to a login page that would send them straight back.

How does Mailbox sanitize email previews?

Every captured email is now parsed into a DOM tree and cleaned node by node before it is displayed.

Earlier versions removed dangerous markup with regular expressions: <script> blocks, double-quoted on* handlers and quoted javascript: URLs. HTML has too many valid spellings for patterns to catch reliably, such as </script >, unquoted handlers, obfuscated schemes, data:text/html links, <meta http-equiv="refresh"> and <base>. 3.0 parses captured HTML with the browser’s DOMParser, which never executes scripts, then works on the tree:

  • active elements are removed
  • every on* attribute is stripped
  • unsafe URL schemes are removed from href, src, action, formaction and similar attributes
  • target and formtarget are removed, and links open in a new tab, so nothing inside an email can navigate the dashboard

The message list snippet is extracted the same way. All of this sits behind the preview iframe’s sandbox, which never allows scripts, so the two layers now protect the dashboard independently. The change also resolves the CodeQL alerts GitHub had raised on the old sanitizer.

How did the dashboard get faster?

The dashboard now loads every attachment on a page with a single query.

Mailbox polls for new mail every five seconds, 20 messages per page. Before 3.0, each poll asked the attachment store for attachments once per message, then once more for every inline image, which added up to dozens of queries per refresh. 3.0 adds findByMessages(array $messageIds) to the AttachmentStore contract. The database driver answers it with one whereIn, and the inline-image rewriter reuses the attachments already loaded. On a page of five messages with two inline images each, attachment queries drop from 15 to 1.

What’s more reliable in Mailbox 3.0?

The release notes list thirteen fixes. The ones teams are most likely to notice:

  • Delivery never waits on capture. When you run Mailbox in decorate mode, capturing locally and forwarding to a real mailer, a capture failure such as a locked SQLite file or a full disk no longer stops the real email. The error is reported and delivery continues.
  • Duplicate sends are recognised. Symfony adds the Message-ID to a clone of the message, so earlier versions never saw it. Mailbox now takes the ID from the sent message, and a resend with the same Message-ID updates the existing capture instead of creating a second one.
  • Attachment bytes stay exactly as sent. Attachment content is always stored as raw bytes, so files that happen to look like base64 are no longer decoded by mistake. Code that holds base64 uses the new AttachmentData::fromBase64().
  • Filenames in any language download correctly. Downloads now build Content-Disposition with Symfony’s HeaderUtils, sending the original name as RFC 5987 filename* with an ASCII fallback. A file called ანგარიში.pdf arrives with its Georgian name intact.
  • Search works the same on every database. Searches containing _ or % now match on SQLite, and Postgres search is case-insensitive through ILIKE, using one explicit ESCAPE clause that works across SQLite, MySQL, Postgres and SQL Server.
  • Setup is smoother. mailbox:install finds its migrations wherever the package is installed, and mailbox:install --dev works in any app and on Windows.

The “Send test email” button now runs through the full capture pipeline, normalizer and attachment storage included, so a successful test confirms the whole path.

How do we keep Mailbox reliable from here?

Alongside the fixes, 3.0 adds guardrails that check every future change.

The package’s architecture tests are now real Pest arch() rules. They enforce layer boundaries between HTTP and storage, which contracts each store implements, naming, strict types and the absence of debug calls, and every file in src/ declares strict_types. A new frontend workflow type-checks and builds the dashboard on every change and fails if the shipped assets differ from a fresh build, so the dashboard you install always matches its source. And the package’s Laravel Boost rules, the guidance AI coding agents read, now describe each command’s real effect, including that mailbox:install --refresh wipes captured messages.

How do I upgrade to Mailbox for Laravel 3.0?

On local development with default settings, the upgrade is two commands:

composer require --dev redberry/mailbox-for-laravel:^3.0
php artisan vendor:publish --tag=mailbox-assets --force

The second command publishes the rebuilt dashboard assets that ship with 3.0. Then check whether any of these apply:

  • Still on Laravel 10? Stay on 2.x until you upgrade Laravel.
  • Use the dashboard on staging or a review app? Define your own viewMailbox gate, as shown above.
  • Wrote a custom attachment driver? Add the bundled fallback trait, which implements the new method with one call per message:
use Redberry\MailboxForLaravel\Storage\Concerns\FindsAttachmentsByMessages;

class RedisAttachmentStore implements AttachmentStore
{
    use FindsAttachmentsByMessages;
}
  • Build AttachmentData with base64 content? Switch to AttachmentData::fromBase64('report.pdf', 'application/pdf', $size, $base64).
  • Type-hint Storage\AttachmentStore? Use Contracts\AttachmentStore, or Storage\DatabaseAttachmentStore for the database implementation.

The full upgrade guide covers each step.

Why is this a major version?

Because 3.0 changes defaults that existing apps may rely on, and semantic versioning says that belongs in a major release.

Teams that open Mailbox on staging today will need a gate after upgrading. Custom attachment drivers need the new method. Laravel 10 drops out of support. Putting those changes behind a new major version means nobody gets them from a routine composer update, and everyone who upgrades does it knowing what to check.

Mailbox for Laravel is open source under the MIT license. The v3.0.0 release notes list every change with its issue, and if you are new to the package, our introduction walks through setup.

Updated on by

Nika Jorjoliani

First published on

Nika Jorjoliani

About the author

Nika Jorjoliani

Head of Engineering at Redberry

Worked on 50+ Laravel Projects

Author of 10+ Open Source Projects

Nika is Engineering Director at Redberry, where he leads delivery across complex Laravel, Vue.js and React products for clients in fintech, wealth management, e-commerce, and SaaS. Over a decade of building large-scale Laravel applications, he has worked across multi-tenant architectures, real-time systems, third-party integrations, DevOps automation, and high-availability infrastructure.

He is the creator and maintainer of several open-source Laravel packages: Mailbox for Laravel, MCP Client for Laravel, and Laravel Packager - and co-organized Georgia's first official Laravel Meetup.

Latest News

Oct
15

Mailbox for Laravel 3.0 Is Out, With Safer Defaults for Captured Email

Mailbox for Laravel, our open-source package for capturing and previewing a Laravel app&rsquo;s email, reached version 3.0 on October 6. It is a security and stability release. The dashboard is now locked to local development by default, email previews are sanitized more safely, and the dashboard loads faster. Teams that open Mailbox on staging need to add one gate before upgrading.

Sep
15

Laravel Meetup Georgia Returns October 8, with Nuno Maduro in Tbilisi

The second edition of Laravel Meetup Georgia is on Thursday, October 8, at 18:00, on the Stamba rooftop in Tbilisi.&nbsp;

Sep
2

Twelve Years of Redberry: From Four Friends in Tbilisi to a Laravel Premier Partner

Redberry is twelve. What began in 2014 with four founders in Tbilisi now rests on two decisions taken along the way: specialise in Laravel, and take that expertise international. That adds up to 300+ products shipped across the EU, UK, US and Middle East, around 200 people, and a place among the 13 Laravel Premier Partners worldwide. Here is how we got here, and who got us here.

img

Meet the authors

We are a 200+ people agency and provide product design, software development, and creative growth marketing services to companies ranging from fresh startups to established enterprises. Our work has earned us 100+ international awards, partnerships with Laravel, Vue, Meta, and Google, and the title of Georgia’s agency of the year in 2019 and 2021.

CONTACT US
<h3>Dati Chkhikvishvili</h3>

Get in touch

Dati Chkhikvishvili

Chief Business Officer