Use Symfony Mailer for real website email. Use PHP mail() only for tiny tests, quick prototypes, or old hosting panels where you cannot install anything better. PHP’s manual says mail() returns true when the mail is accepted for delivery, not when it reaches the inbox [source: PHP manual]. That tiny detail causes many “but it worked on my server” headaches.
TLDR: mail() is the pocket knife. Symfony Mailer is the toolbox. If a shop sends 2,000 order emails a month and only 3% vanish, that is 60 confused customers asking, “Where is my receipt?” For a contact form, mail() may be fine. For password resets, invoices, quotes, bookings, and marketing alerts, use Symfony Mailer.
The simple version
Website email sounds easy.
A user fills a form. Your site sends a message. Everyone smiles.
Then the email lands in spam. Or never arrives. Or arrives without accents. Or arrives five hours late. Nice.
This is why the choice matters.
- PHP
mail()is built into PHP. - Symfony Mailer is a modern email library.
mail()is simple, but blunt.- Symfony Mailer needs setup, but gives control.
Think of mail() as yelling a letter at the post office door. Think of Symfony Mailer as walking in with tracking, labels, receipts, and a bored clerk who still does the job.
What PHP mail() actually does
The mail() function asks the server to send an email.
That is it.
It does not log into Gmail for you. It does not talk nicely to Mailgun, SendGrid, Amazon SES, or Postmark by default. It relies on the server’s mail setup. On Linux, that often means Sendmail, Postfix, or a similar mail transfer agent.
Here is the classic example:
mail(
"customer@example.com",
"Your order",
"Thanks for your order!",
"From: shop@example.com"
);
Looks clean. Feels lovely. Maybe too lovely.
The catch is that mail() gives very little feedback. If it returns true, the server accepted the message. That does not mean the customer received it. It does not even mean the email left the server in a healthy state.
Where mail() is fine
mail() is not evil. It is just old and basic.
Use it when the stakes are low.
- A local development test.
- A tiny personal site.
- A one-off internal alert.
- A cheap shared host with no Composer access.
If one message fails, nobody cries. Nobody opens a support ticket. Nobody loses a booking.
That is the safe zone.
Where mail() gets annoying
Honestly, it feels like mail() was built for a quieter internet.
Modern email is fussy. Very fussy.
Mail providers check SPF. They check DKIM. They check DMARC. They judge your sender reputation. They sniff weird headers. They dislike sloppy HTML. They dislike strange line endings. They dislike you personally on a Tuesday.
mail() does not help much with this.
You may hit problems like:
- No SMTP authentication built into the function.
- Poor error details when delivery fails.
- Harder HTML email creation.
- Manual headers that are easy to mess up.
- No clean retry system for failed sends.
- No transport switching without custom code.
Expect to waste time on tiny things. A missing header can cost 20 minutes. A server setting can cost two hours. A spam folder mystery can eat your whole afternoon.
What Symfony Mailer does better
Symfony Mailer is built for real email work.
It lets you send through SMTP or email services. It supports attachments, HTML, plain text, headers, embedded images, and safer message building. It also works well with Symfony apps, but you can use it outside Symfony too.
Symfony’s docs show support for many transports, including SMTP and third-party providers [source: Symfony].
Here is a tiny taste:
$email = (new Email())
->from('shop@example.com')
->to('customer@example.com')
->subject('Your order')
->text('Thanks for your order!')
->html('<p><strong>Thanks</strong> for your order!</p>');
$mailer->send($email);
It reads nicely. It separates parts of the message. It is harder to create a messy blob of headers by accident.
The big win: better sending options
With mail(), your server is the main actor.
With Symfony Mailer, you can use a proper email provider.
That matters a lot.
A provider can give you:
- SMTP login with a username and password.
- Higher deliverability when DNS is set well.
- Bounce tracking in many services.
- Delivery logs you can read without crying.
- Rate limits that protect your account.
- Better IP reputation than a random bargain server.
For example, a booking site sending 300 confirmation emails per day needs proof. If 12 guests say they never got the email, the owner needs logs. “PHP returned true” is not proof. It is a shrug in code form.
Security and header mess
Email headers are picky. They are also a place where bad input can cause trouble.
If you build headers by hand, you must clean user input. Never trust a name, subject, or email address from a form. People paste weird stuff. Bots paste worse stuff.
With mail(), beginners often do this:
$headers = "From: " . $_POST["email"];
Nope. Bad idea.
Validate it first. Escape it. Better yet, use a library that handles the message format with more care.
Symfony Mailer does not make bad code impossible. Nothing does. But it does make good code easier.
HTML emails and attachments
Plain text is easy.
Modern website emails are not always plain.
You may need:
- A logo.
- An invoice PDF.
- A reset button.
- A booking table.
- A plain text fallback.
Doing this with mail() means building MIME parts yourself or using extra code. That can get ugly fast.
Symfony Mailer handles these jobs in a cleaner way. Attach a file. Add HTML. Add text. Move on with your life.
Speed and background sending
Email can slow a page down.
If your checkout waits while the server sends three emails, users may feel it. Even a delay of 1.5 seconds feels clunky during payment.
Symfony Mailer can work with queues through Symfony Messenger. That means your app can accept the order now and send the email in the background. The user sees the thank-you page faster.
mail() can be quick too. But when stuff fails, or the server stalls, you have fewer tools.
So which one should you choose?
Pick PHP mail() if:
- You are testing.
- The site is tiny.
- Emails are not business critical.
- You cannot install packages.
Pick Symfony Mailer if:
- You send customer emails.
- You need password resets.
- You send invoices or receipts.
- You care about logs.
- You want SMTP or an email API.
- You need templates and attachments.
Final verdict
mail() is okay for small jobs. It is not a full email system.
Symfony Mailer is the better pick for serious websites. It gives structure. It supports real senders. It makes HTML, attachments, and provider setup much less painful.
If email matters to your users, do not gamble with a silent function and crossed fingers. Use Symfony Mailer, set up SPF and DKIM, test your forms, and keep your logs. Your future self will be less grumpy.
