SMTP voor Python — mail uit uw script of Django-app die aankomt
Python heeft SMTP ingebouwd: met smtplib verstuurt u een bericht in een handvol regels, en Django bundelt dat in een eigen mail-laag met EMAIL-instellingen. Wat geen van beide levert, is afleverbaarheid — die hangt af van de server waarlangs u verstuurt.
Of u nu een los script, een Flask-app of een Django-project hebt: de instellingen zijn dezelfde vijf gegevens. U wijst ze naar uw relay en de code die de berichten opbouwt, blijft ongewijzigd.
Waarom het misgaat zonder relay
Python-mail komt vaak uit de achtergrond: een nachtelijk rapport, een cron-taak die facturen verstuurt, een script dat waarschuwt wanneer een meting buiten de marge valt. Niemand zit erop te kijken — en dus merkt niemand dat de ontvangende server ze stilletjes weigert. Bij Django komt daar de klassieke livegang-verrassing bij: in ontwikkeling gaat alles naar de console, in productie naar echte providers die wél op reputatie controleren.
SMTP instellen in Python
Gegevens ophalen
Log in op uw 2mail-account en haal daar uw SMTP-server, gebruikersnaam en wachtwoord op.
Invullen in Python
In een Django-project zet u de EMAIL-instellingen in de settings: de SMTP-backend als e-mailbackend, de host van de relay, poort 587, de STARTTLS-optie aan, de gebruikersnaam en het wachtwoord, en een standaard afzenderadres op uw eigen domein. Werkt u rechtstreeks met smtplib, dan maakt u verbinding met de host op poort 587, roept u starttls aan, logt u in met gebruikersnaam en wachtwoord en verstuurt u daarna het bericht. Lees de gegevens in beide gevallen uit omgevingsvariabelen, niet uit de code.
Domein authenticeren
Zorg dat SPF, DKIM en DMARC voor uw domein correct staan, zodat ontvangers kunnen verifiëren dat de mail van u komt.
Testen
Verstuur vanuit uw platform een testbericht naar een adres bij een grote provider en controleer of het in de inbox aankomt en niet in de spammap.
De velden die Python vraagt
Elk platform noemt ze net iets anders, maar het gaat altijd om dezelfde vijf. De concrete waarden zijn accountgebonden en vindt u in uw 2mail-account — vul ze daar op, zodat u zeker de juiste gebruikt.
| Veld | Wat u invult |
|---|---|
| SMTP-server (host) | De servernaam uit uw 2mail-account. |
| Poort | De poort die bij uw gekozen versleuteling hoort; uw account toont welke. |
| Versleuteling | TLS. Verstuur nooit onversleuteld — uw inloggegevens gaan anders leesbaar over de lijn. |
| Gebruikersnaam | De SMTP-login van uw 2mail-account. |
| Wachtwoord | Het bijbehorende wachtwoord. Gebruik nooit het wachtwoord van een mailbox van een medewerker. |
SMTP-configuratie voor Python in code
Werkende voorbeelden voor poort 587 met STARTTLS, klaar om over te nemen. Vervang het afzenderadres door een adres op uw eigen domein en controleer servernaam en login in uw 2mail-account. Het wachtwoord hoort in een omgevingsvariabele — nooit in de code en nooit in versiebeheer.
smtplib: verbinden op 587 met starttls()
De standaardbibliotheek volstaat. De volgorde telt: eerst starttls(), dan pas login() — anders gaat uw wachtwoord onversleuteld over de lijn, of weigert de relay de aanmelding. Gebruik smtplib.SMTP en niet SMTP_SSL: die laatste hoort bij poort 465. Login en wachtwoord komen uit omgevingsvariabelen.
import os
import smtplib
import ssl
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "Example <noreply@example.com>"
msg["To"] = "you@example.org"
msg["Subject"] = "SMTP test"
msg.set_content("Sent through the 2mail SMTP relay.")
context = ssl.create_default_context()
with smtplib.SMTP("relay.2mail.eu", 587, timeout=10) as server:
server.starttls(context=context) # upgrade to TLS first, log in afterwards
server.login(os.environ["SMTP_USERNAME"], os.environ["SMTP_PASSWORD"])
server.send_message(msg) # call again for the next message, same connection Django: de EMAIL_*-settings
Django heeft geen extra pakket nodig. Zet de SMTP-backend expliciet: in ontwikkeling staat die vaak op de console-backend, en dan verstuurt productie niets terwijl alles lijkt te werken. sendtestemail is een ingebouwd beheercommando waarmee u de instellingen controleert zonder een view of taak te moeten aanroepen.
# settings.py
import os
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "relay.2mail.eu"
EMAIL_PORT = 587
EMAIL_USE_TLS = True # STARTTLS - never together with EMAIL_USE_SSL
EMAIL_HOST_USER = os.environ["SMTP_USERNAME"]
EMAIL_HOST_PASSWORD = os.environ["SMTP_PASSWORD"]
EMAIL_TIMEOUT = 10
DEFAULT_FROM_EMAIL = "Example <noreply@example.com>"
SERVER_EMAIL = "noreply@example.com" # sender of error reports to ADMINS
# Test from the shell:
# python manage.py sendtestemail you@example.org Flask-Mail: dezelfde vijf gegevens
Werkt u met Flask, dan vraagt Flask-Mail dezelfde gegevens onder andere namen. Ook hier geldt: MAIL_USE_TLS voor poort 587, en MAIL_USE_SSL blijft uit.
import os
from flask import Flask
from flask_mail import Mail
app = Flask(__name__)
app.config.update(
MAIL_SERVER="relay.2mail.eu",
MAIL_PORT=587,
MAIL_USE_TLS=True, # STARTTLS
MAIL_USE_SSL=False,
MAIL_USERNAME=os.environ["SMTP_USERNAME"],
MAIL_PASSWORD=os.environ["SMTP_PASSWORD"],
MAIL_DEFAULT_SENDER="noreply@example.com",
)
mail = Mail(app) Welke mails Python verstuurt
Zodra de relay ingesteld is, lopen deze allemaal via 2mail — u hoeft ze niet stuk voor stuk te configureren.
Valkuilen bij Python
TLS-optie en poort moeten kloppen
Django kent twee aparte TLS-instellingen: één voor STARTTLS en één voor TLS vanaf het begin. Zet enkel de STARTTLS-instelling aan; die hoort bij poort 587. Beide tegelijk geeft een configuratiefout; geen van beide stuurt uw login onversleuteld, en dat weigert een relay terecht.
Eén verbinding per reeks, niet per bericht
Een script dat voor elk van duizend berichten een nieuwe SMTP-verbinding opent, gedraagt zich als een aanvaller. Open één verbinding, verstuur de reeks, sluit af. Django heeft daarvoor een functie om meerdere berichten over één verbinding te sturen.
Stille fouten in achtergrondtaken
Een uitzondering in een cron-taak verdwijnt in een logbestand dat niemand leest. Vang fouten bij het versturen op en meld ze ergens waar ze opvallen — en koppel de webhooks van 2mail voor aflevering en bounce, zodat u ook ziet wat ná de overdracht misloopt.
Veelvoorkomende foutmeldingen bij Python
De meldingen die u te zien krijgt wanneer host, poort, versleuteling of login niet kloppen — letterlijk, zodat u ze herkent in uw log, met de oorzaak en de oplossing ernaast.
| Melding | Oorzaak | Oplossing |
|---|---|---|
smtplib.SMTPAuthenticationError: (535, b'5.7.8 Error: authentication failed: …') | De relay weigert de login: een verkeerde gebruikersnaam of een verkeerd wachtwoord, of een omgevingsvariabele die in de cron- of servicecontext een andere waarde heeft dan in uw shell. | Druk af wat het proces effectief leest (de gebruikersnaam, nooit het wachtwoord) en gebruik de SMTP-login uit uw 2mail-account — niet het wachtwoord van een mailbox. |
smtplib.SMTPNotSupportedError: SMTP AUTH extension not supported by server. | login() wordt aangeroepen vóór starttls(). Een relay biedt aanmelden pas aan zodra de verbinding versleuteld is; op de nog onversleutelde verbinding bestaat AUTH dus niet. | Roep eerst server.starttls(context=context) aan en dan pas server.login(…). In Django: zet EMAIL_USE_TLS = True. |
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:…) | De code gebruikt smtplib.SMTP_SSL (of in Django EMAIL_USE_SSL) op poort 587. Python begint dan meteen met een TLS-handdruk, terwijl de relay op die poort eerst gewone SMTP verwacht en pas na STARTTLS overschakelt. De klassieke 465-versus-587-verwarring. | Gebruik smtplib.SMTP(…, 587) gevolgd door starttls(), of in Django EMAIL_USE_TLS = True met poort 587. |
smtplib.SMTPServerDisconnected: Connection unexpectedly closed | De verbinding is weggevallen vóór of tijdens het gesprek. Meestal een script dat een verbinding openhoudt en ze na een lange pauze opnieuw gebruikt, terwijl de server ze intussen wegens inactiviteit heeft gesloten — of een gewone SMTP-verbinding naar een poort die TLS vanaf de eerste byte verwacht. | Open één verbinding per reeks berichten en sluit ze daarna af; vang de uitzondering op en verbind opnieuw. Controleer dat u poort 587 met starttls() gebruikt. |
ValueError: EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set one of those settings to True. | In de Django-settings staan beide TLS-opties aan — vaak omdat één ervan uit een gedeeld basisbestand komt en de andere uit de productie-settings. | Laat enkel EMAIL_USE_TLS = True staan en zet EMAIL_USE_SSL = False. |
Ander platform?
SMTP is een standaardprotocol: elk systeem dat SMTP spreekt — een CRM, een ERP, een eigen applicatie of een server — kan via 2mail versturen met dezelfde vijf velden.