HTML E-Mail with PHP: 5 Pointers to Get ‘er DONE!
Sending e-mail can be pretty useful with PHP. It's also really easy... until you want to send HTML that is.
I used these two tutorials to learn how to send HTML e-mails in PHP:
sitepoint.com advanced e-mail tutorial
webcheatsheets.com sending e-mail tutorial
For something that requires very strict accurate guidelines to be followed, you would think they wouldn't just gloss over them or ignore them entirely. But they did.
So, here are five pointers to get you off on the right foot:
1. Use Content-Type: multipart/alternative; with a random hash
Don't just send the e-mail as HTML. Many e-mail providers don't allow for HTML e-mails, so you need alternative content. You will need a boundary string, but you will also want it to be random hash. Why? Well, I'm not really sure. I can easily imagine one scenario, which would be that if you are sending mail on behalf of someone, and they figure out your static boundary string, they could do some sort of injection attack and send some nasty thing through with your e-mail. Not really sure if this would work, or if this is the reason, but everyone else uses a random hash, so you should too!
<?php
//create a boundary string. It must be unique
//so we use the MD5 algorithm to generate a random hash
$random_hash = md5(date('r', time()));
//define the headers we want passed. Note that they are separated with \r\n
$headers = "From: webmaster@example.com\r\nReply-To: webmaster@example.com";
//add boundary string and mime type specification
$headers .= "\r\nContent-Type: multipart/alternative; boundary=\"PHP-alt-$random_hash\"";
?>
2. Don't use HEREDOC syntax
At first I was trying to just do this:
$msg <<<HERE --PHP-alt-$random_hash Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Plain text stuff --PHP-alt-$random_hash Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: 7bit <html> <body> <p> Blah blah blah $var blah! </p> </body> </html> --PHP-alt-$random_hash-- HERE;
But it just wouldn't work! I figured out you have to do it like this:
$message = "--PHP-alt-$random_hash\n\r" . "Content-Type: text/plain; charset=\"iso-8859-1\"" . "Content-Transfer-Encoding: 7bit\n\r" . "Hello World!!! \n\r" . "This is simple text email message. \n\r" . "--PHP-alt-$random_hash\n" . "Content-Type: text/html; charset=\"iso-8859-1\"" . "Content-Transfer-Encoding: 7bit\n\r" . "<h2>Hello World!</h2>\n\r" . "<p>This is something with <b>HTML</b> formatting.</p> \n\r" . "--PHP-alt-$random_hash--";
Pay special attention to where you are putting your \ns and \rs!!!
3. Get the from right!
When building your headers, don't forget to include a from. This will make your e-mail more recognizable and professional. Don't just include the from though, include a human readable value as well!
$headers = "From: An actual name <email@email.com>\r\n";
4. Don't forget those trailing "--" after the final bondary!
Don't forget those last two dashes on your final boundary:
--==Multipart_Boundary_{$semi_rand}x--
See them there at the end? You don't put those on the others, so they are easy to forget on the final one. Also, it's boundary with an "a", not "boundry"... Yeah, I misspelled it the first time...
5. If things aren't working, experiment
It took me a good hour and a half to get this working right. It all came down to those little \r and \n things in the end. I had to carefully add and remove them from different places until I got everything just right. The webcheatsheet code conveniently uses some buffer trickery to avoid this, but my web host does not support that, so I had to figure it all out myself. Here is a version of their code that actually works on my web hose (Dreamhost):
<?php
//define the receiver of the email
$to = 'email@gmail.com';
//define the subject of the email
$subject = 'Test HTML email';
//create a boundary string. It must be unique
//so we use the MD5 algorithm to generate a random hash
$random_hash = md5(date('r', time()));
//define the headers we want passed. Note that they are separated with \r\n
$headers = "From: webmaster@example.com\r\nReply-To: webmaster@example.com";
//add boundary string and mime type specification
$headers .= "\r\nContent-Type: multipart/alternative; boundary=\"PHP-alt-$random_hash\"";
//define the body of the message.
$message = "--PHP-alt-$random_hash\n\r" .
"Content-Type: text/plain; charset=\"iso-8859-1\"" .
"Content-Transfer-Encoding: 7bit\n\r" .
"Hello World!!! \n\r" .
"This is simple text email message. \n\r" .
"--PHP-alt-$random_hash\n" .
"Content-Type: text/html; charset=\"iso-8859-1\"" .
"Content-Transfer-Encoding: 7bit\n\r" .
"<h2>Hello World!</h2>\n\r" .
"<p>This is something with <b>HTML</b> formatting.</p> \n\r" .
"--PHP-alt-$random_hash--";
//send the email
$mail_sent = mail( $to, $subject, $message, $headers );
//if the message is sent successfully print "Mail sent". Otherwise print "Mail failed"
echo $mail_sent ? "Mail sent" : "Mail failed";
echo $message;
?>
Have fun! Feel free to comment if you have any other tips/questions.






October 8th, 2008 - 00:17
Why re-invent the wheel? Use PHPMailer!
October 8th, 2008 - 04:52
Maybe that should have been pointer #1.
Problem being that it isn’t a standard part of PHP, so I didn’t know about it. It does look like a good library though… Thanks!
March 16th, 2010 - 11:16
cool thanks for posting this
April 7th, 2010 - 11:03
Hi there cos i so totally dig your great site, i wuold feel very special if you would want me to publish a site review about your great website in my iPod Newssite http://kh3.us would you grant me permission to? XoX,
April 7th, 2010 - 11:06
lol, sure, go for it! Thanks.
May 14th, 2010 - 15:44
I’m happy I found this blog site, I couldnt discover any info on this subject matter before. thanks
September 12th, 2010 - 04:07
i thought this was a really great article to read. i’ll check back for new posts by you!
July 7th, 2012 - 16:15
The reason for using a unique string in the header boundary is to allow for nested multipart messages. If a multipart mail were to include another multipart mail and they happened to use the same boundary string, it would be impossible to determine where the included content ended. Some mailers generate boundary strings based on various bits of information that are lying around; it doesn’t have to be random and it doesn’t have to be a hash. A good source for a unique boundary string would be to generate a UUID; that capability is available on all platforms, now.
PEAR/Mail_mime.php composes multipart messages neatly, and you don’t have to fiddle with all the CRLF’s. Dreamhost only provides a minimal set of PEAR modules, but you can install your own. See http://wiki.dreamhost.com/PEAR#Installing_Packages