23 Jahre aus Stockholm
Operator-Notizen · Aus dem Stockholm-Büro

SMTP-Grundlagen für Application Developer

Application Developers schicken regelmäßig E-Mails aus ihren Anwendungen heraus, aber die meisten haben nie gelernt was unter der Haube passiert. SMTP-Protokoll-Sequenz, Status-Codes, konkrete Code-Beispiele.

Application Developers schicken regelmäßig E-Mails aus ihren Anwendungen heraus — Bestellbestätigungen, Passwort-Resets, Benachrichtigungen. Aber die meisten Developer haben nie gelernt, was unter der Haube passiert.

Was SMTP eigentlich ist

SMTP (Simple Mail Transfer Protocol, RFC 5321) ist ein 1982 standardisiertes Protokoll, das definiert wie E-Mails übertragen werden. Es ist textbasiert und hat sich seit den 1980ern erstaunlich wenig geändert.

S: 220 mail.example.com ESMTP Postfix
C: EHLO client.ihr-unternehmen.de
S: 250-mail.example.com Hello
S: 250-STARTTLS
S: 250 AUTH LOGIN PLAIN
C: STARTTLS
S: 220 Ready to start TLS
[TLS handshake]
C: EHLO client.ihr-unternehmen.de
C: AUTH PLAIN [base64-encoded credentials]
S: 235 Authentication successful
C: MAIL FROM: <noreply@ihr-unternehmen.de>
S: 250 OK
C: RCPT TO: <empfaenger@example.com>
S: 250 OK
C: DATA
S: 354 Send message
C: From: noreply@ihr-unternehmen.de
C: Subject: Test
C: 
C: Hallo Welt
C: .
S: 250 Message accepted (queued as ABCD1234)
C: QUIT

Status-Codes: 2xx = success, 3xx = continue, 4xx = temporary failure (retry), 5xx = permanent failure.

Warum Status-Codes wichtig sind

2xx (Success): der Mail-Server hat die Mail akzeptiert. Das bedeutet NICHT, dass sie beim Empfänger angekommen ist — nur, dass der nächste MTA in der Kette sie übernommen hat.

4xx (Temporary failure): Server kann die Mail jetzt nicht akzeptieren, später wieder versuchen. Beispiele: 4.7.0 (Server überlastet), 4.7.1 (Rate-Limit), 4.2.1 (Greylisting).

5xx (Permanent failure): Mail wird nicht akzeptiert. Beispiele: 5.1.1 (Mailbox existiert nicht), 5.1.2 (Empfänger-Domain ungültig), 5.7.1 (Spam-Klassifikation). Empfänger sollte aus aktiven Listen entfernt werden.

Code-Beispiel: Python mit smtplib

import smtplib
from email.message import EmailMessage
from email.utils import formataddr

def send_order_confirmation(recipient_email, order_id, total_amount):
    msg = EmailMessage()
    msg['From'] = formataddr(('Ihr Unternehmen', 'noreply@ihr-unternehmen.de'))
    msg['To'] = recipient_email
    msg['Subject'] = f'Bestellbestätigung #{order_id}'
    msg['Reply-To'] = 'support@ihr-unternehmen.de'
    msg.set_content(f"Vielen Dank für Ihre Bestellung #{order_id}.\nBetrag: {total_amount} EUR")
    
    try:
        with smtplib.SMTP_SSL('smtp.authorizehosting.com', 465, timeout=10) as smtp:
            smtp.login('your_username', 'your_password')
            smtp.send_message(msg)
            return True
    except smtplib.SMTPRecipientsRefused as e:
        # 5xx - permanent
        suppression_list.add(recipient_email)
        return False
    except (smtplib.SMTPServerDisconnected, ConnectionError) as e:
        # Netzwerk - retry
        retry_queue.add(msg)
        return False

Code-Beispiel: Node.js mit nodemailer

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
    host: 'smtp.authorizehosting.com',
    port: 465,
    secure: true,
    auth: {
        user: process.env.SMTP_USER,
        pass: process.env.SMTP_PASS
    },
    pool: true,
    maxConnections: 5,
    maxMessages: 100,
    rateLimit: 10
});

async function sendOrderConfirmation(recipientEmail, orderId, totalAmount) {
    try {
        const info = await transporter.sendMail({
            from: '"Ihr Unternehmen" <noreply@ihr-unternehmen.de>',
            to: recipientEmail,
            replyTo: 'support@ihr-unternehmen.de',
            subject: `Bestellbestätigung #${orderId}`,
            text: `Vielen Dank für Ihre Bestellung #${orderId}. Betrag: ${totalAmount} EUR`
        });
        return true;
    } catch (error) {
        if (error.responseCode >= 500) {
            await suppressionList.add(recipientEmail);
        } else if (error.responseCode >= 400) {
            await retryQueue.add({ recipientEmail, orderId, totalAmount });
        }
        return false;
    }
}

Code-Beispiel: Java mit Spring Boot

# application.properties
spring.mail.host=smtp.authorizehosting.com
spring.mail.port=587
spring.mail.username=${SMTP_USERNAME}
spring.mail.password=${SMTP_PASSWORD}
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.connectiontimeout=10000

@Service
public class EmailService {
    @Autowired
    private JavaMailSender mailSender;
    
    public boolean sendOrderConfirmation(String recipientEmail, Long orderId) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("noreply@ihr-unternehmen.de");
        message.setTo(recipientEmail);
        message.setSubject(String.format("Bestellbestätigung #%d", orderId));
        message.setText(String.format("Vielen Dank für Ihre Bestellung #%d", orderId));
        
        try {
            mailSender.send(message);
            return true;
        } catch (MailSendException e) {
            log.error("Mail send failed", e);
            return false;
        }
    }
}

Häufige Developer-Fehler

Fehler 1: Synchroner Versand im Request-Handler. Mail-Versand kann Sekunden dauern. Niemals im Request-Response-Zyklus blockieren — immer asynchron via Queue.

Fehler 2: Keine Retry-Logik. 4xx-Fehler werden ignoriert, Mail geht verloren. Exponential backoff für 4xx, kein Retry für 5xx.

Fehler 3: Connection nicht wiederverwenden. Bei Hochvolumen Connection-Pooling nutzen.

Fehler 4: Hardcoded Credentials. SMTP-Username/Passwort im Code-Repository ist Sicherheits-Risiko. Environment-Variables oder Secret-Manager nutzen.

Fehler 5: Kein Reply-To. Empfänger-Antworten landen in einem Postfach, das niemand überwacht.

Fehler 6: HTML ohne Plain-Text-Alternative. Manche Spam-Filter werten HTML-only Mails negativer.

Fehler 7: Keine Suppression-Verarbeitung. Empfänger mit Hard-Bounce wird wieder versendet.

Wann SMTP, wann Email API?

Für Standard-Anwendungsfälle reicht SMTP. Email API ist strukturell überlegen bei: Webhook-Events (Open, Click, Bounce in Echtzeit), Idempotency-Garantien, detailliertes Tracking pro Mail, Template-Management auf Anbieter-Seite.

Bei Authorize Hosting unterstützen wir beide ohne Mehrkosten.

SMTP-Setup für Ihre Anwendung professionell aufsetzen?

Wir prüfen Ihre SMTP-Integration und schlagen Verbesserungen vor — Connection-Pooling, Retry-Logik, Suppression-Verarbeitung.