Send an Email with Attachments in PHP
Every web application uses the mail feature to send emails to subscribers for various purposes, like sending newsletters, verification emails, etc. Learn how to send an attachment with an email in PHP.

Sending emails from a web application is one of the most commonly used features. Web applications send emails to users for registration and verification, password recovery, transactional emails, marketing and promotional emails and reminder emails. In this post, we will learn how to upload files and send an email with multiple attachments in PHP in simple and easy steps.
PHP has a built-in mail() function to send emails from a website. To enhance the functionality of this function, we need to make some changes to the message body in order to send attachments in an email from a website.
bool mail($to_email, $subject, $message, $headers, $parameters);
$to_email: The email address to which the email will be sent.$subject: The subject of the email.$message: The message body of the email, either plain text or HTML.$headers: Headers of email like From, CC, BCC and Reply-To.$parameters: The additional parameters that can be used for the command-line tool sending the email.
We will create an HTML form with To, From, Subject and message fields to send an email. We will also add a file input field to upload files and send them as attachments in the email. Files that are going to be created for this post are:
- index.php: An HTML page containing the email form with an input file field.
- send-email.php: The PHP script to send an email with attachments.
- javascript.js: The JavaScript file to initialize the TinyMCE editor on the textarea field.
- style.css: The CSS stylesheet for the HTML page and form.
Create an HTML Form to Send Email
Create an HTML page with a form containing the needed form to send an email. The following fields will be added to the form:
- An email field to enter the email address "To" to which the email will be sent.
- An email field to enter the email address for the "From" address.
- A text field for the subject of the email.
- An input file field with a multiple attribute to allow multiple file uploads.
- A textarea field with the TinyMCE editor to support rich text HTML.
- The form must have the attribute
enctype=multipart/form-datafor file uploads.
index.php
<?php
if (!session_id()) {
session_start();
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Send an Email with Attachment in PHP - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport"/>
<link rel="stylesheet" href="css/style.css"/>
<script src="js/jquery-3.1.1.min.js"></script>
<script src="plugins/tinymce/js/tinymce/tinymce.min.js"></script>
<script src="js/javascript.js" type="text/javascript"></script>
</head>
<body>
<section class="section py-4">
<div class="container">
<?php if (isset($_SESSION['message'])) { ?>
<div class="alert alert-<?= $_SESSION['status']; ?>">
<?= $_SESSION['message']; ?>
</div>
<?php
unset($_SESSION['message'], $_SESSION['status']);
} ?>
<form action="send-email.php" method="POST" enctype="multipart/form-data" class="email-form">
<div class="mb-4">
<label class="inline-block mb-1">To email address: <span class="text-red">*</span></label>
<input type="email" name="from" class="form-control" required="required"
placeholder="Email address..."/>
</div>
<div class="mb-4">
<label class="inline-block mb-1">From email address: <span class="text-red">*</span></label>
<input type="email" name="to" class="form-control" required="required" placeholder="Email address..."/>
</div>
<div class="mb-4">
<label class="inline-block mb-1">Subject: <span class="text-red">*</span></label>
<input type="text" name="subject" class="form-control" required="required" placeholder="Subject..."/>
</div>
<div class="mb-4">
<label class="inline-block mb-1">Attach files</label>
<input type="file" name="attachments[]" class="form-control" multiple="multiple"/>
</div>
<div class="mb-4">
<label class="inline-block mb-1">Email message: <span class="text-red">*</span></label>
<textarea name="message" class="form-control tinymce" required="required"
placeholder="Message..."></textarea>
</div>
<div class="mb-4">
<button type="submit" class="btn btn-blue">Send</button>
</div>
</form>
</div>
</section>
</body>
</html>
Send an Email with an Attachment (PHP Script)
Create a script to add files as attachments in a message and send the email using the PHP mail() function. The logical flow is to set the multipart/mixed content type in the header, start a boundary for the message body and for each file start a separate boundary. The following are the steps to send attachments with email in PHP by extending the message body using boundaries and using the built-in mail function of PHP:
- Start a session to store the message and status of the message.
- Sanitize the
$_POSTarray and store it in a variable. - Create a unique ID for the boundary.
- Set headers for the email including MIME-Version, Content-Type, From and Reply-To.
- Start a boundary for email message with
Content-Type: text/html, charset=UTF-8,Content-Transfer-Encoding: Quot-Printedand finally add the message sent in the form submission. - Loop through each uploaded file and start a boundary for each file.
- Add
Content-Type: application/octet-stream; filename=$file_nameto the message. - Add
Content-Description: $file_nameto the message. - Add
Content-Transfer-Encoding: base64to the message. - Add
Content-Disposition: attachment; filename=$file_nameto the message. - Add the base64-encoded content of the uploaded file to the email message.
- Close the boundary of the entire message with the
$uidvariable. - Send the email using the PHP mail function.
- Set the message and status in the session based on whether the email was sent or not.
- Redirect the user to the HTML form page after the email is sent, regardless of the success or failure of delivery.
send-email.php
<?php
if (!session_id()) {
session_start();
}
$post = filter_input_array(INPUT_POST, [
'to' => FILTER_SANITIZE_EMAIL,
'from' => FILTER_SANITIZE_EMAIL,
'subject' => FILTER_DEFAULT,
'message' => FILTER_DEFAULT,
]);
$uid = md5(uniqid(time()));
// Set headers for email
$headers = [
'MIME-Version' => '1.0',
'Content-Type' => sprintf("%s boundary=\"%s\"", 'multipart/mixed;', $uid),
'From' => sprintf('<%s>', $post['from']),
'Reply-To' => sprintf('<%s>', $post['from']),
];
// Add boundary for email message
$message = sprintf("--%s\r\n", $uid);
$message .= sprintf("Content-type: %s\r\n", 'text/html; charset=UtF-8');
$message .= sprintf("Content-Transfer-Encoding: %s\r\n\r\n", 'Quot-Printed');
$message .= sprintf("%s\r\n\r\n", stripcslashes($post['message']));
if (!empty($_FILES)) {
for ($i = 0; $i < count($_FILES['attachments']['name']); $i++) {
// If there is an error in uploaded file then continue and skip this file
if ($_FILES['attachments']['error'][$i] > 0) {
continue;
}
// Store file name in a variable
$file_name = $_FILES['attachments']['name'][$i];
// Get the file content of current file in loop
$file_content = file_get_contents($_FILES['attachments']['tmp_name'][$i]);
// Add boundary for each attachment file
$message .= sprintf("--%s\r\n", $uid);
$message .= sprintf("Content-Type: application/octet-stream; name=\"%s\"\r\n", $file_name);
$message .= sprintf("Content-Description: %s\r\n", $file_name);
$message .= sprintf("Content-Transfer-Encoding: %s\r\n", 'base64');
$message .= sprintf("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", $file_name);
$message .= sprintf("%s\r\n\r\n", chunk_split(base64_encode($file_content)));
}
}
// Close over all message boundary
$message .= sprintf("--%s--", $uid);
if (mail($post['to'], $post['subject'], $message, $headers)) {
$_SESSION['message'] = 'Email has been successfully sent';
$_SESSION['status'] = 'green';
} else {
$_SESSION['message'] = 'Failed to send email';
$_SESSION['red'] = 'red';
}
header('Location: ' . $_SERVER['HTTP_REFERER']);
exit;
Enable TinyMCE Editor on TextArea
Add JavaScript code to initialize the TinyMCE editor to support rich HTML text.
javascript.js
$(document).ready(function () {
tinymce.init({
branding: false,
selector: '.tinymce',
height: '350',
required: true,
showPreview: true,
setup: function (editor) {
editor.on('change', function (e) {
editor.save();
});
}
});
});Add CSS Styles
Add CSS rules for the entire HTML page and email form fields.
style.css
* {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
}
body {
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
}
.container {
max-width: 1024px;
margin: 0 auto;
padding-left: 15px;
padding-right: 15px;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.my-1, .mb-1 {
margin-bottom: .25rem;
}
.my-4, .mb-4 {
margin-bottom: 1rem;
}
.inline-block {
display: inline-block;
}
.text-red {
color: #e65442;
}
.alert {
position: relative;
padding: .75rem 1.25rem;
margin-bottom: 1rem;
transition:opacity 0.5s;
}
.alert-green {
background-color: #319764;
border: 1px solid #248A57;
color: #ffffff;
}
.alert-red {
background-color: #e65442;
border: 1px solid #d94735;
color: #ffffff;
}
.form-control {
display: block;
width: 100%;
padding: .375rem .75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #1c2528;
background-color: #ffffff;
background-clip: padding-box;
border: 1px solid #d1d2d3;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.form-control:focus {
border-color: #00c0ef;
outline: 0;
}
.btn {
display: inline-block;
padding: .375rem .75rem;
cursor: pointer;
font-size: inherit;
}
.btn-blue {
background-color: #0369a1;
border: 1px solid #0369a1;
color: #ffffff;
}
.btn-blue:hover {
background-color: #005D95;
}