symfony/amazon-mailer
Symfony Mailer transport for Amazon SES. Configure SES via DSNs for SMTP, HTTPS, or API with region, optional session token, and port-based TLS behavior (implicit TLS or STARTTLS with optional require_tls override).
Install the Package Add the Symfony Mailer and Amazon Mailer packages via Composer:
composer require symfony/mailer symfony/amazon-mailer
Configure AWS SES Credentials
Add the DSN to your .env file. Choose one of the supported schemes:
# SMTP (recommended for most use cases)
MAIL_MAILER=amazon_ses
MAILER_DSN=ses+smtp://AKIAEXAMPLE:SECRETKEY@default?region=us-east-1&session_token=SESSION_TOKEN
# HTTPS (direct API calls)
MAILER_DSN=ses+https://AKIAEXAMPLE:SECRETKEY@default?region=us-east-1
# API (alternative to HTTPS)
MAILER_DSN=ses+api://AKIAEXAMPLE:SECRETKEY@default?region=us-east-1
AKIAEXAMPLE and SECRETKEY with your AWS IAM user credentials.region to your AWS SES region (e.g., us-east-1, eu-west-1).session_token.Verify SES Configuration Ensure your AWS SES account is verified for the sender email addresses and domains you plan to use. SES requires verification before sending emails in production.
First Email Send
Use Laravel’s Mail facade to send an email:
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
Mail::to('user@example.com')->send(new WelcomeEmail('John Doe'));
Transport Configuration
Laravel’s config/mail.php can be updated to use the Symfony Mailer transport:
'default' => env('MAIL_MAILER', 'amazon_ses'),
'amazon_ses' => [
'transport' => env('MAILER_DSN', 'ses+smtp://default'),
],
Alternatively, configure the DSN directly in .env (as shown above).
Customizing Email Headers Add SES-specific headers (e.g., for list management or tracking):
$email = (new WelcomeEmail())
->withSwiftMessage(function ($message) {
return $message
->getHeaders()
->addTextHeader('X-SES-LIST-MANAGEMENT-OPTIONS', '{"Enable": true}');
});
Handling SES Notifications (Bounces/Complaints) Use AWS SNS to subscribe to SES notifications and process them in Laravel:
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class ProcessSesNotification implements ShouldQueue
{
use Dispatchable, Queueable;
public function handle() {
// Parse SNS message and update user records (e.g., mark email as bounced)
}
}
Fallback Transport for Development
Use a different transport (e.g., log or array) in .env during development:
MAIL_MAILER=array
MAILER_DSN=null
Testing with SES Sandbox Test emails in AWS SES Sandbox (requires verified identities):
// Use a verified sender in tests
Mail::to('verified@example.com')->send(new WelcomeEmail());
Laravel Mailables: Extend Laravel’s Mailable class to leverage Symfony Mailer features:
use Symfony\Component\Mailer\MailerInterface;
use Illuminate\Mail\Mailable;
class WelcomeEmail extends Mailable
{
public function build()
{
return $this->markdown('emails.welcome')
->withSwiftMessage(function ($message) {
$message->getHeaders()
->addTextHeader('Reply-To', 'support@example.com');
});
}
}
Retry Logic: SES may throttle requests. Configure Symfony Mailer’s retry strategy in config/mail.php:
'amazon_ses' => [
'transport' => env('MAILER_DSN'),
'retry_strategy' => [
'max_retries' => 3,
'delay' => 1000, // milliseconds
],
],
Logging: Enable Symfony Mailer logging to debug issues:
// In a service provider
$this->app->bind(MailerInterface::class, function ($app) {
$mailer = new Mailer(new AmazonMailerTransport(env('MAILER_DSN')));
$mailer->on('message', function ($message) {
\Log::debug('Sent message:', ['subject' => $message->getSubject()]);
});
return $mailer;
});
SES Sandbox Restrictions
TLS/STARTTLS Configuration
ses+smtp with port 587 (STARTTLS) may fail if the SES server does not support it. Port 465 (implicit TLS) is more reliable.ses+smtp://...?port=465 or explicitly set require_tls=1:
MAILER_DSN=ses+smtp://...?port=465&require_tls=1
Custom Headers Encoding
addTextHeader for safe encoding:
$message->getHeaders()->addTextHeader('Custom-Header', 'Value');
IAM Permissions
ses:SendEmail, ses:SendRawEmail, and ses:SendBulkTemplatedEmail permissions.AmazonSESFullAccess policy or create a custom policy with minimal permissions.Rate Limits
Laravel-Specific Quirks
Mail facade may not expose all Symfony Mailer features (e.g., custom transports). Use Symfony’s MailerInterface directly for advanced use cases:
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Transport\AmazonMailerTransport;
$mailer = new Mailer(new AmazonMailerTransport(env('MAILER_DSN')));
$mailer->send((new Email())->to('user@example.com')->subject('Test'));
Enable Verbose Logging
Add this to your .env to debug SMTP/API issues:
SYMFONY_MAILER_DEBUG=true
Check SES Metrics Monitor SES metrics in AWS CloudWatch for delivery stats, bounces, and complaints.
Test with telnet
Verify SMTP connectivity manually:
telnet ses.smtp.us-east-1.amazonaws.com 465
Custom Transport
Extend AmazonMailerTransport to add Laravel-specific logic:
use Symfony\Component\Mailer\Transport\AmazonMailerTransport as BaseTransport;
class LaravelAmazonMailerTransport extends BaseTransport
{
public function __construct(string $dsn, array $options = [])
{
parent::__construct($dsn, $options);
// Add Laravel-specific logic (e.g., event listeners)
}
}
Event Listeners Subscribe to Symfony Mailer events to log or modify messages:
use Symfony\Component\Mailer\EventListener\MessageEvent;
$mailer->on('message', function (MessageEvent $event) {
\Log::info('Sending email to: ' . $event->getMessage()->getTo());
});
SES Templates Use SES templates for dynamic emails:
$message = (new Email())
->to('user@example.com')
->subject('Welcome!')
->html('<h1>Hello!</h1>')
->getSwiftMessage()
->getHeaders()
->addTextHeader('X-SES-MESSAGE-TAGS', 'welcome,template=welcome-template');
**
How can I help you explore Laravel packages today?