~9 min read • Updated Mar 15, 2026
Introduction
Django offers a clean and convenient API for sending email, built on top of Python’s built‑in smtplib.
These wrappers simplify email delivery, improve testing during development, and support environments where SMTP is not available.
All email utilities live in the django.core.mail module.
Quick Email Sending with send_mail()
The simplest way to send email in Django is using send_mail().
This function is ideal for sending plain text messages quickly.
from django.core.mail import send_mail
send_mail(
"Subject here",
"Here is the message.",
"[email protected]",
["[email protected]"],
fail_silently=False,
)
Required Parameters
- subject: Email subject (string)
- message: Plain text body
- from_email: Sender address
- recipient_list: List of recipients
Optional Keyword Arguments
- fail_silently: Suppress SMTP errors
- auth_user: Override SMTP username
- auth_password: Override SMTP password
- connection: Custom email backend
- html_message: HTML version of the email
The return value is the number of successfully delivered messages (0 or 1).
Advanced Emails with EmailMessage and EmailMultiAlternatives
For more control—attachments, custom headers, HTML content—use EmailMessage or EmailMultiAlternatives.
Sending Multipart Emails (HTML + Text)
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
text_content = render_to_string(
"templates/emails/my_email.txt",
{"my_variable": 42},
)
html_content = render_to_string(
"templates/emails/my_email.html",
{"my_variable": 42},
)
msg = EmailMultiAlternatives(
subject="Subject here",
body=text_content,
from_email="[email protected]",
to=["[email protected]"],
headers={"List-Unsubscribe": ""},
)
msg.attach_alternative(html_content, "text/html")
msg.send()
This approach is ideal for newsletters, transactional emails, and template‑based messages.
SMTP Configuration
Django sends email using the following settings:
- EMAIL_HOST: SMTP server hostname
- EMAIL_PORT: SMTP port
- EMAIL_HOST_USER: Username
- EMAIL_HOST_PASSWORD: Password
- EMAIL_USE_TLS: Enable TLS
- EMAIL_USE_SSL: Enable SSL
The character set defaults to DEFAULT_CHARSET.
Mass Emailing with send_mass_mail()
Use send_mass_mail() to send multiple emails efficiently using a single SMTP connection.
Example
message1 = (
"Subject here",
"Here is the message",
"[email protected]",
["[email protected]", "[email protected]"],
)
message2 = (
"Another Subject",
"Here is another message",
"[email protected]",
["[email protected]"],
)
send_mass_mail((message1, message2), fail_silently=False)
Each tuple becomes a separate email message. The return value is the number of successfully delivered messages.
Best Practices
- Use EmailMultiAlternatives for HTML emails.
- Use send_mass_mail() for bulk sending.
- Store SMTP credentials securely (environment variables).
- Use console or file email backends during development.
- Always set fail_silently=False in production to detect errors.
Conclusion
Django’s email utilities provide a powerful yet simple interface for sending everything from basic notifications to rich HTML emails.
With tools like send_mail(), EmailMessage, and EmailMultiAlternatives, you can build reliable, flexible, and secure email workflows for any application.
send_mail() vs send_mass_mail()
The key difference between send_mail() and send_mass_mail() is how they handle SMTP connections:
- send_mail() opens a new SMTP connection for every email.
- send_mass_mail() opens a single connection and sends all messages through it.
This makes send_mass_mail() more efficient when sending multiple emails.
mail_admins()
The mail_admins() function sends an email to all site administrators defined in the ADMINS setting.
Features
- Automatically prefixes the subject with
EMAIL_SUBJECT_PREFIX(default: “[Django] ”). - Uses
SERVER_EMAILas the “From:” address. - Supports
html_messagefor sending HTML emails.
This function exists for convenience and readability.
mail_managers()
mail_managers() works exactly like mail_admins(), but sends email to the addresses listed in the MANAGERS setting.
Examples
Sending one email to multiple recipients (all appear in “To:”)
send_mail(
"Subject",
"Message.",
"[email protected]",
["[email protected]", "[email protected]"],
)
Sending separate emails to each recipient
datatuple = (
("Subject", "Message.", "[email protected]", ["[email protected]"]),
("Subject", "Message.", "[email protected]", ["[email protected]"]),
)
send_mass_mail(datatuple)
Preventing Header Injection
Header injection is a security attack where an attacker inserts newline characters into email headers to manipulate “To:” or “From:” fields.
Django protects against this by raising ValueError if any header value contains a newline.
Safe Example
def send_email(request):
subject = request.POST.get("subject", "")
message = request.POST.get("message", "")
from_email = request.POST.get("from_email", "")
if subject and message and from_email:
try:
send_mail(subject, message, from_email, ["[email protected]"])
except ValueError:
return HttpResponse("Invalid header found.")
return HttpResponseRedirect("/contact/thanks/")
return HttpResponse("Make sure all fields are entered and valid.")
The EmailMessage Class
The send_mail() and send_mass_mail() functions are thin wrappers around the EmailMessage class.
To use advanced features such as:
- BCC recipients
- CC recipients
- Reply-To headers
- File attachments
- Multipart emails
you must create EmailMessage objects directly.
Example
from django.core.mail import EmailMessage
email = EmailMessage(
subject="Hello",
body="Body goes here",
from_email="[email protected]",
to=["[email protected]", "[email protected]"],
bcc=["[email protected]"],
reply_to=["[email protected]"],
headers={"Message-ID": "foo"},
)
To send the email:
email.send()
Conclusion
Django provides a flexible and powerful email system. Use send_mail() for simple messages, send_mass_mail() for efficient bulk sending, and EmailMessage for full control over email structure. Functions like mail_admins() and mail_managers() make system notifications easy and secure.
Introduction
Django’s email framework goes far beyond simple send_mail() usage.
For advanced email composition—attachments, inline images, HTML alternatives, custom headers, and backend control—Django provides the powerful EmailMessage and EmailMultiAlternatives classes.
EmailMessage Methods
send(fail_silently=False)
Sends the email. If a connection was provided, it will be reused; otherwise, Django creates a new backend connection. Returns:
- 1 → message sent successfully
- 0 → message not sent
If fail_silently=True, exceptions are suppressed.
message(policy=email.policy.default)
Builds and returns a Python email.message.EmailMessage object.
The policy argument controls serialization rules (e.g., SMTP line endings).
Override this method when subclassing EmailMessage to customize the underlying MIME structure.
recipients()
Returns a combined list of all recipients from:
- to
- cc
- bcc
If you add custom recipient fields in a subclass, override this method.
Handling Attachments
attach(filename, content, mimetype)
Two usage patterns:
1. Standard attachment
message.attach("design.png", img_data, "image/png")
2. MIMEPart attachment (Django 6.0+)
Useful for inline images or attachments requiring custom headers.
import email.utils
from email.message import MIMEPart
cid = email.utils.make_msgid()
inline_image = MIMEPart()
inline_image.set_content(
image_data_bytes,
maintype="image",
subtype="png",
disposition="inline",
cid=cid,
)
message.attach(inline_image)
message.attach_alternative(f'
', "text/html")
Support for legacy MIMEBase is deprecated.
attach_file(path, mimetype=None)
Attach a file directly from disk:
message.attach_file("/images/weather_map.png")
EmailAttachment (Django 5.2+)
A named tuple with:
- filename
- content
- mimetype
Used internally to store attachments.
Sending Multiple Content Types
EmailMultiAlternatives
A subclass of EmailMessage that supports multiple body formats (e.g., text + HTML).
Example
from django.core.mail import EmailMultiAlternatives
msg = EmailMultiAlternatives(
"hello",
"This is an important message.",
"[email protected]",
["[email protected]"],
)
msg.attach_alternative("This is an important message.
", "text/html")
msg.send()
alternatives attribute
A list of EmailAlternative named tuples (content, mimetype).
body_contains(text)
Checks whether the given text appears in:
- the main body
- all text/* alternatives
Useful for testing.
Changing the Default Content Type
By default, EmailMessage uses text/plain. To send HTML as the main body:
msg = EmailMessage(subject, html_content, from_email, [to])
msg.content_subtype = "html"
msg.send()
Email Backends
The backend handles the actual sending of messages.
Backend Methods
- open() → opens a long-lived connection
- close() → closes the connection
- send_messages() → sends a list of EmailMessage objects
Using a backend as a context manager
from django.core import mail
with mail.get_connection() as connection:
mail.EmailMessage(subject1, body1, from1, [to1], connection=connection).send()
mail.EmailMessage(subject2, body2, from2, [to2], connection=connection).send()
This ensures efficient reuse of the same SMTP connection.
Conclusion
Django’s advanced email system provides everything needed for professional-grade email delivery: attachments, inline images, HTML alternatives, custom headers, and backend control.
With EmailMessage, EmailMultiAlternatives, and flexible backends, you can build robust and scalable email workflows for any application.
Obtaining an Email Backend Instance
The get_connection() function in django.core.mail returns an instance of the email backend you want to use.
from django.core.mail import get_connection
connection = get_connection()
Parameters
- backend: Optional import path to override EMAIL_BACKEND
- fail_silently: If True, suppresses exceptions during sending
- Other keyword arguments → passed directly to the backend constructor
If no backend is specified, Django uses the EMAIL_BACKEND setting.
Built‑in Django Email Backends
1. SMTP Backend (Default)
Sends real emails through an SMTP server.
Settings used:
- EMAIL_HOST
- EMAIL_PORT
- EMAIL_HOST_USER
- EMAIL_HOST_PASSWORD
- EMAIL_USE_TLS
- EMAIL_USE_SSL
- EMAIL_TIMEOUT
- EMAIL_SSL_KEYFILE
- EMAIL_SSL_CERTFILE
Explicit configuration:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
2. Console Backend
Writes emails to stdout instead of sending them. Ideal for development.
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
3. File Backend
Writes each email to a file on disk.
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = "/tmp/app-messages"
4. In‑Memory Backend (locmem)
Stores emails in django.core.mail.outbox. Perfect for testing.
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
5. Dummy Backend
Does nothing—emails are ignored. Useful for development.
EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
Creating a Custom Email Backend
If you need custom email‑sending behavior, you can write your own backend.
Custom backends must:
- Subclass
BaseEmailBackend - Implement send_messages(email_messages)
If your backend maintains a persistent connection, also implement:
- open()
- close()
The smtp.EmailBackend class is a good reference implementation.
Sending Multiple Emails Efficiently
Opening and closing SMTP connections is expensive. Django provides two ways to reuse a connection.
Method 1: Using send_messages()
Send a list of messages using a single connection.
from django.core import mail
connection = mail.get_connection()
messages = get_notification_email()
connection.send_messages(messages)
Method 2: Manually opening and closing the connection
from django.core import mail
connection = mail.get_connection()
connection.open()
email1 = mail.EmailMessage("Hello", "Body", "[email protected]", ["to1"], connection=connection)
email1.send()
email2 = mail.EmailMessage("Hello", "Body", "[email protected]", ["to2"])
email3 = mail.EmailMessage("Hello", "Body", "[email protected]", ["to3"])
connection.send_messages([email2, email3])
connection.close()
Email Configuration for Development
During development, you often want to inspect emails without actually sending them.
Recommended options
- Console backend → prints emails to terminal
- File backend → writes emails to disk
- LocMem backend → stores emails in memory for tests
Using a local SMTP server with aiosmtpd
python -m pip install "aiosmtpd >= 1.4.5"
python -m aiosmtpd -n -l localhost:8025
This server prints all email headers and bodies to stdout.
Just set EMAIL_HOST and EMAIL_PORT accordingly.
Conclusion
Django’s email backend system provides flexibility for every scenario—from real SMTP delivery to development‑friendly console and file backends.
With get_connection(), custom backend support, and efficient multi‑email sending, Django gives you full control over how emails are delivered in your application.
Written & researched by Dr. Shahin Siami