Tutorials10 min read

Wiring JustEmails SMTP into Django Auth for Verification and Reset Mail

Configure Django's EMAIL_BACKEND with JustEmails SMTP credentials for account verification, password resets, and other auth emails that actually land in inboxes.

By JustEmails Platform Team

I got a Slack message at 2am last month: "Users aren't getting password reset emails." The Django app was configured correctly — or so I thought. Turns out the SMTP settings were fine, the emails were sending, but every single one landed in spam. No SPF record. The domain had been live for six months like that. Nobody noticed until a customer complaint.

This is the setup guide I wish I'd had. Not just the Django settings (that's three lines), but the full path from EMAIL_BACKEND to an email that actually reaches the inbox, authenticates properly, and doesn't get your domain blacklisted.

Look, I've set up SMTP in Django probably fifty times. Still manage to mess something up about half the time.

What We're Building

By the end of this, you'll have Django sending account verification emails, password resets, and any other auth-related mail through JustEmails SMTP. The emails will authenticate with SPF and DKIM, which means they'll land in inboxes instead of spam folders. You'll also have a testing workflow that doesn't require burning through real sends.

Prerequisites

  • Django 4.0+ (works on 3.2 LTS too, same settings)
  • A JustEmails account with SMTP credentials
  • A custom domain with DNS access (you'll add SPF/DKIM records)
  • Basic familiarity with Django settings and environment variables
  • Optional: django-allauth if you're using it for auth (the SMTP config is the same either way)

If you're also adding analytics to your Django app, check out our Django analytics middleware tutorial for a lightweight tracking setup.

Don't have SMTP credentials yet? JustEmails includes full SMTP access on the $49/year plan — unlimited domains, unlimited mailboxes. Grab yours from the dashboard under Settings > SMTP/IMAP Access.

Step 1: Configure Django's Email Backend

Open your settings.py (or wherever you keep environment-specific settings). Add these:

# settings.py

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.justemails.app'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
EMAIL_HOST_USER = os.environ.get('SMTP_USER')
EMAIL_HOST_PASSWORD = os.environ.get('SMTP_PASS')
DEFAULT_FROM_EMAIL = 'YourApp <noreply@yourdomain.com>'
SERVER_EMAIL = 'server@yourdomain.com'

That's the core config. Let me explain why each line matters.

EMAIL_PORT = 587 — This is the standard submission port for authenticated SMTP. Port 465 works too (with EMAIL_USE_SSL=True and EMAIL_USE_TLS=False), but 587 with STARTTLS is more universally supported.

EMAIL_USE_TLS = True — This enables STARTTLS encryption. Your credentials and email content are encrypted in transit. Non-negotiable in 2026. (I'm genuinely baffled that some tutorials still show examples without TLS. It's not 2008 anymore.)

DEFAULT_FROM_EMAIL — What users see in their inbox. Use your actual domain, not a freemail address. Sending from yourname@gmail.com via your own SMTP is a massive spam signal.

SERVER_EMAIL — Where Django sends error reports (500 errors, broken links). I usually set this to a separate address so I can filter them.

Step 2: Store Credentials Securely

Never hardcode SMTP passwords in settings.py. I've seen production credentials committed to public GitHub repos more times than I can count.

Create a .env file (add it to .gitignore):

# .env
SMTP_USER=your-smtp-username@justemails.app
SMTP_PASS=your-smtp-password

Load it with python-dotenv or django-environ:

# settings.py
from dotenv import load_dotenv
load_dotenv()

EMAIL_HOST_USER = os.environ.get('SMTP_USER')
EMAIL_HOST_PASSWORD = os.environ.get('SMTP_PASS')

If you're on Heroku, Railway, Render, or similar — use their secrets management instead. Same environment variable names, just set them in the dashboard rather than a file. (We compared Railway vs Vercel vs Fly.io recently if you're evaluating platforms.)

(I spent an embarrassing amount of time debugging "SMTP auth failed" once. Turned out I had a trailing space in the password from copy-pasting. Strip your inputs.)

Step 3: Verify Email Authentication Records

Here's where most Django tutorials stop. And it's exactly where deliverability problems start.

Honestly? This is the part I hate. DNS is tedious, propagation is unpredictable, and one wrong TXT record can break everything. But skip it and your emails are toast.

Your emails might send successfully — SMTP returns 250 OK — but land in spam because the receiving server can't verify you're authorized to send from that domain. You need three DNS records: SPF, DKIM, and DMARC.

Check your current records:

# SPF
dig TXT yourdomain.com +short | grep spf

# DKIM (replace selector with yours — JustEmails uses 'default')
dig TXT default._domainkey.yourdomain.com +short

# DMARC
dig TXT _dmarc.yourdomain.com +short

If you're using JustEmails, the dashboard shows your required DNS records under Domains > [your domain] > DNS Records. Add them to your DNS provider (Cloudflare, Route53, Namecheap, wherever).

Example SPF record:

v=spf1 include:spf.justemails.app ~all

Example DKIM: JustEmails auto-generates your DKIM key. Copy the full TXT value from the dashboard.

Example DMARC (start permissive):

v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com

DNS propagation takes up to 48 hours, but usually 15-30 minutes with most providers. Usually. I've been burned by Cloudflare's aggressive caching before — clear it manually if you're impatient. Or just wait. Grab lunch. The DNS gods are fickle and do not respond to refreshing.

For a full DMARC rollout strategy (moving from p=none to p=reject without breaking legitimate mail), see our DMARC enforcement guide.

Step 4: Test Locally Without Sending

Before hitting your SMTP quota, test with Django's console backend:

# settings.py (development only)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Now run the Django shell:

from django.core.mail import send_mail

send_mail(
    'Test Subject',
    'Test body.',
    'noreply@yourdomain.com',
    ['you@example.com'],
    fail_silently=False,
)

You'll see the full email printed in your terminal — headers, body, everything. Great for debugging template issues before you involve actual SMTP.

For staging environments, use the file backend:

EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = '/tmp/app-emails'

Emails get written as .log files. You can inspect them, test your templates, verify personalization — all without sending a single real email.

My unpopular opinion: most Django apps don't need a separate email testing service like Mailtrap. The file backend does 90% of what you need for free. Fight me.

Step 5: Test Real Delivery

Once local testing looks good, switch to SMTP and send a real email:

# settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# ... rest of SMTP config
# Django shell
from django.core.mail import send_mail

send_mail(
    'Django SMTP Test',
    'If you got this, SMTP is working.',
    'noreply@yourdomain.com',
    ['your-personal-email@gmail.com'],
    fail_silently=False,
)

Check three things:

  1. Did it arrive? Check spam folder too.
  2. Did it authenticate? In Gmail, click the three dots > "Show original" and look for spf=pass, dkim=pass, dmarc=pass.
  3. Does the From address match? The "From" header should show your domain, not the SMTP server's domain.

If SPF or DKIM show "fail" or "softfail," go back to Step 3. Your DNS records aren't right, or they haven't propagated yet. Don't panic. I've stared at "softfail" for an hour only to have it magically resolve itself. Email infrastructure is held together with duct tape and optimism.

Step 6: Wire Up Django Auth Emails

Django's built-in auth and django-allauth both use DEFAULT_FROM_EMAIL automatically. Once your SMTP is configured, password resets and account verification just work.

For django-allauth specifically, you might want to customize the from address per email type:

# settings.py
ACCOUNT_EMAIL_SUBJECT_PREFIX = '[YourApp] '
DEFAULT_FROM_EMAIL = 'YourApp <noreply@yourdomain.com>'

# Or override per-email in your allauth templates

Test the full flow:

  1. Create a new user (or use the password reset form)
  2. Check if the verification/reset email arrives
  3. Click the link — make sure the token works
  4. Check the email headers for authentication pass

If you're running into token expiration issues, that's a Django setting (PASSWORD_RESET_TIMEOUT), not an SMTP issue. The email is delivered correctly; the link just expires too fast. Default is 3 days (259200 seconds).

Common Errors and Fixes

SMTPAuthenticationError: (535, b'Authentication failed')

Wrong credentials. Copy-paste them fresh from the JustEmails dashboard. Check for trailing spaces or newlines.

Still not working? Regenerate the password entirely. Sometimes credential rotation gets out of sync. Annoying but fixable in thirty seconds.

SMTPServerDisconnected: Connection unexpectedly closed

Usually a TLS mismatch. Make sure EMAIL_USE_TLS=True and EMAIL_USE_SSL=False for port 587. Or you're behind a corporate firewall that blocks outbound SMTP. Try from a different network to confirm.

socket.timeout: timed out

Your hosting provider might block outbound SMTP. Common on shared hosting, some AWS configurations, and Heroku's free tier. Check with your host, or switch to an HTTP API approach instead.

Emails send but land in spam

Check authentication. Run your domain through mail-tester.com — it shows exactly what's missing. Usually it's SPF, DKIM, or sending from a domain that doesn't match your SMTP credentials. If you're comparing Google Workspace alternatives, make sure any replacement supports proper authentication.

BadHeaderError: Header values can't contain newlines

Your subject or from-email has a newline character. This happens when user input sneaks into email headers (a security issue — someone could inject arbitrary headers). Sanitize your inputs.

Next Steps

Now that auth emails are flowing, you probably want to:

  • Monitor deliverability — Sign up for Google Postmaster Tools and watch your domain reputation. Spike in spam complaints? You'll see it here before it tanks your deliverability. If you're running campaigns, consider click fraud detection to ensure your traffic is legitimate.

  • Add retry logic — SMTP can fail transiently. Use django-celery-email to queue emails and retry failures automatically.

  • Move to DMARC enforcement — Start with p=none, analyze your DMARC reports, then ramp to p=quarantine and eventually p=reject. We cover this in detail in our DMARC enforcement guide.

  • Track email engagement — SMTP alone doesn't give you open/click data. If you need analytics, check out our comparison of SMTP vs API for transactional email — the API path adds webhook events.

If you're also setting up custom domain email hosting (not just transactional SMTP), we have a full custom domain email setup guide that covers mailboxes, MX records, and migrating from Google Workspace.

Questions? Hit us at support@justemails.app. We're actually monitoring that inbox — not a ticketing black hole.

(And if you find an error in this guide, tell me. I've probably already made a newer, more creative mistake somewhere else.)

Frequently Asked Questions

Why are my Django password reset emails going to spam?

Usually it's missing email authentication — your sending domain doesn't have SPF, DKIM, or DMARC configured. SMTP providers will accept and send the message, but receiving servers (Gmail, Outlook) see no authentication and flag it as suspicious. Check your DNS records first. Second common cause: you're sending from a freemail address (gmail.com, outlook.com) via your own SMTP server — that's a red flag for spam filters.

Should I use port 587 or 465 for Django SMTP?

Port 587 with STARTTLS (EMAIL_USE_TLS=True, EMAIL_USE_SSL=False) is the standard for client-to-server submission. Port 465 uses implicit SSL (EMAIL_USE_SSL=True, EMAIL_USE_TLS=False) and works but is less universally supported. Avoid port 25 — it's for server-to-server relay and most cloud providers block it.

Can I use the same SMTP credentials for Django and other apps?

Yes. Your JustEmails SMTP credentials work anywhere that supports standard SMTP — Django, Rails, Laravel, WordPress, or any other framework. Same host, same port, same username and password. That's the point of SMTP being a standard protocol.

How do I test Django email without sending real emails?

Set EMAIL_BACKEND to 'django.core.mail.backends.console.EmailBackend' during development — emails print to your terminal instead of sending. For staging, use 'django.core.mail.backends.filebased.EmailBackend' with EMAIL_FILE_PATH to write emails to disk. Switch to SMTP only when you need to test actual delivery.


Try JustEmails

Unlimited custom domain email hosting for $49/year flat — unlimited domains, unlimited mailboxes, 10 GB storage, full IMAP/SMTP. Built for agencies, freelancers, and anyone managing email across more than one domain.

Start your 7-day free trial → · How it compares

django-smtpemail-authenticationdjango-allauthpassword-resettransactional-emailbuildinpublicsaasstudioaiworkforcebuildwithclaude

Related posts