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:
composer require symfony/amazon-mailer
Ensure symfony/mailer is also installed (required dependency).
Configure .env:
Use one of the DSN formats from the README. Example for SMTP:
MAIL_MAILER=amazon_ses
MAILER_DSN=ses+smtp://ACCESS_KEY:SECRET_KEY@default?region=us-east-1&port=465
Replace ACCESS_KEY, SECRET_KEY, and region with your AWS SES credentials and preferred region.
Verify AWS SES Setup:
First Email Send:
Use Laravel’s Mail facade as usual. The package integrates seamlessly with Symfony Mailer under the hood:
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
Mail::to('user@example.com')->send(new WelcomeEmail());
Check Logs:
Monitor Laravel logs (storage/logs/laravel.log) for SES-specific errors or delivery statuses.
Transactional Emails:
ses+api transport for low-latency sends (e.g., password resets, order confirmations).MAILER_DSN=ses+api://ACCESS_KEY:SECRET_KEY@default?region=us-west-2
Email class for structured emails:
$email = (new Email())
->to('user@example.com')
->subject('Your Order Confirmation')
->html(view('emails.order_confirmation', ['order' => $order]));
Mail::send($email);
Marketing Campaigns:
ses+smtp for bulk sends (e.g., newsletters) with STARTTLS for security:
MAILER_DSN=ses+smtp://ACCESS_KEY:SECRET_KEY@default?region=eu-west-1&port=587
Mail::batch():
Mail::batch([])->to($recipients)->send(new Newsletter());
Dynamic Headers:
$email->getHeaders()->addTextHeader('X-SES-MESSAGE-TAGS', 'newsletter=campaign1');
Fallback Mechanism:
config/mail.php for failover:
'transports' => [
'amazon_ses' => [
'dsn' => env('MAILER_DSN', 'ses+smtp://...'),
],
'fallback' => [
'dsn' => env('FALLBACK_MAILER_DSN', 'smtp://...'),
],
],
public function handle($request, Closure $next) {
if (config('app.env') === 'production' && !SES::isHealthy()) {
config(['mail.mailers.amazon_ses' => 'fallback']);
}
return $next($request);
}
AWS IAM Roles: For serverless environments (e.g., Lambda), use IAM roles instead of access keys:
MAILER_DSN=ses+api://@default?region=us-east-1
Ensure the role has ses:SendEmail, ses:SendRawEmail, and ses:SendTemplatedEmail permissions.
SES Templates: Pre-configure templates in AWS SES and reference them in Laravel:
$email->templateId('YOUR_TEMPLATE_ID');
$email->templateData(['name' => 'John']);
Event Notifications: Subscribe to SES SNS topics for bounce/complaint events:
use Aws\Ses\SesClient;
$ses = new SesClient(['region' => 'us-east-1']);
$ses->subscribe([
'Protocol' => 'sqs',
'Topic' => 'arn:aws:sns:us-east-1:123456789012:your-topic',
'Endpoint' => 'https://sqs.us-east-1.amazonaws.com/123456789012/your-queue',
]);
Testing: Use SES Sandbox for development/testing. Verify emails in the AWS SES console under "Test and Verify Emails."
SES Sandbox Restrictions:
TLS/STARTTLS Misconfigurations:
465 (implicit TLS) may fail if the server doesn’t support it. Port 587 (STARTTLS) is more widely supported but requires require_tls=1 by default.require_tls=0 in the DSN only if you trust your network:
MAILER_DSN=ses+smtp://...&port=587&require_tls=0
Custom Headers Encoding:
ses+api. Ensure proper encoding:
$email->getHeaders()->addTextHeader('X-Custom-Header', mb_convert_encoding($value, 'UTF-8'));
Rate Limits:
SendBulkTemplatedEmail for high-volume sends.Laravel-Specific Quirks:
Mail facade may not expose all Symfony Mailer features (e.g., Email class methods). Use Symfony’s MailerInterface directly for advanced use cases:
use Symfony\Component\Mailer\MailerInterface;
$mailer = app(MailerInterface::class);
$mailer->send($email);
Enable Verbose Logging:
Add this to config/mail.php to debug transport issues:
'logging' => true,
'sendmail' => '/usr/sbin/sendmail -bs',
Check SES Event Publish Metrics: Use AWS CloudWatch to verify if emails are being published to SNS topics:
aws cloudwatch get-metric-statistics \
--namespace AWS/Ses \
--metric-name Send \
--dimensions Name=SourceIp,Value=YOUR_IP \
--start-time $(date -u -v-15M +"%Y-%m-%dT%H:%M:%SZ") \
--end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--period 60 \
--statistics Sum
Validate DKIM/SPF: Use tools like MXToolbox to verify your domain’s DKIM and SPF records. Misconfigurations can cause emails to land in spam.
Custom Transport:
Extend Symfony’s AmazonSesTransport for Laravel-specific logic:
namespace App\Mail\Transports;
use Symfony\Component\Mailer\Transport\AmazonSesTransport as BaseTransport;
class LaravelAmazonSesTransport extends BaseTransport {
public function __construct(string $dsn, array $options = []) {
parent::__construct($dsn, $options);
// Add Laravel-specific logic (e.g., queue job for retries)
}
}
Register it in config/mail.php:
'transports' => [
'amazon_ses' => [
'dsn' => env('MAILER_DSN'),
'class' => App\Mail\Transports\LaravelAmazonSesTransport::class,
],
],
Middleware for SES Features: Create middleware to inject SES-specific headers or metadata:
namespace App\Http\Middleware;
use Closure;
use Symfony\Component\Mailer\Email;
class AddSesMetadata {
public function handle($request, Closure $next) {
$email = $request->mail;
if ($email instanceof Email) {
$email->getHeaders()->addTextHeader('X-SES-CUSTOM
How can I help you explore Laravel packages today?