Files
einkaufsapp/docs/mail-zustellbarkeit.md
2026-08-08 20:31:58 +02:00

116 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Zustellbarkeit ausgehender Mails
Die Anwendung setzt alle Kopfzeilen korrekt, aber ob eine Mail im Posteingang
oder im Spam landet, entscheidet sich überwiegend im DNS und beim Relay nicht
im Anwendungscode. Diese Notiz trennt beides sauber.
## Was die Anwendung erledigt
| Kopfzeile | Warum |
|---|---|
| `Date` | Fehlt sie, werten praktisch alle Filter ab |
| `Message-ID` | Mit der Domain aus `SMTP_ENVELOPE_FROM`, damit sie zur DKIM-Signatur passt |
| `From` mit Anzeigename | RFC-konform kodiert über `email.headerregistry` |
| `Reply-To` | Optional, wenn Antworten woanders hin sollen |
| `Auto-Submitted: auto-generated` | RFC 3834 unterdrückt Abwesenheitsschleifen |
| `X-Auto-Response-Suppress: All` | Dasselbe für Exchange/Outlook |
| EHLO-Hostname | Aus `SMTP_HELO_HOSTNAME` statt der Container-ID |
| Envelope-From getrennt vom `From` | SPF wird gegen den Envelope geprüft |
Der Versand wiederholt sich bei temporären Fehlern mit wachsendem Abstand
(2 s, 4 s, 8 s). Bei permanenter Ablehnung Empfänger unbekannt, Absender
abgelehnt, Authentifizierung fehlgeschlagen bricht er sofort ab, statt das
Relay weiter zu belasten.
## Was du im DNS einrichten musst
Ohne diese drei Einträge landen Mails auch bei perfektem Code im Spam. Alle
beziehen sich auf die Domain aus `SMTP_ENVELOPE_FROM`.
**SPF** erlaubt deinem Relay, für die Domain zu senden:
```
example.de. IN TXT "v=spf1 mx a:mail.example.de -all"
```
`-all` (hard fail) ist strenger als `~all` und wird von Empfängern besser
bewertet. Setze es erst, wenn du sicher bist, dass alle legitimen Absender
erfasst sind.
**DKIM** signiert ausgehende Mails. Die Signatur erzeugt das Relay, nicht
diese Anwendung. In Postfix über OpenDKIM oder rspamd, in mailcow ist es
eingebaut. Der öffentliche Schlüssel gehört ins DNS:
```
selector._domainkey.example.de. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBg..."
```
Wichtig: Die signierende Domain (`d=` in der Signatur) muss zur Domain im
`From`-Header passen, sonst schlägt die DMARC-Ausrichtung fehl.
**DMARC** sagt Empfängern, was bei Fehlschlägen passieren soll:
```
_dmarc.example.de. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.de; adkim=s; aspf=s"
```
Fang mit `p=none` an und wertet die Reports ein paar Wochen aus, bevor du auf
`quarantine` oder `reject` gehst.
**PTR (Reverse DNS)** die IP deines Relays muss auf einen Namen auflösen, der
vorwärts wieder auf dieselbe IP zeigt. Bei Hetzner setzt du das im
Cloud-Console-Interface bzw. im Robot. Fehlt der PTR, weisen einige große
Anbieter direkt beim `MAIL FROM` ab.
## Ausrichtung von From und Envelope-From
DMARC verlangt, dass mindestens eine der beiden Prüfungen *ausgerichtet* ist:
- **SPF-Ausrichtung:** Domain in `SMTP_ENVELOPE_FROM` = Domain in `SMTP_FROM`
- **DKIM-Ausrichtung:** signierende Domain = Domain in `SMTP_FROM`
Mit `adkim=s; aspf=s` im DMARC-Record verlangst du exakte Übereinstimmung
(nicht nur die Organisationsdomain). Das ist strenger und sicherer, bedeutet
aber: `bounces@example.de` und `einkaufsapp@example.de` sind ausgerichtet,
`bounces@bounce.example.de` wäre es nicht mehr.
## Testen
Vor dem ersten echten Versand:
```bash
# Verbindung zum Relay prüfen, ohne etwas zu senden
curl -s -b cookies.txt localhost:8000/api/admin/mail/check | jq
# Testnachricht auslösen
CSRF=$(grep ea_csrf cookies.txt | awk '{print $7}')
curl -s -b cookies.txt -H "X-CSRF-Token: $CSRF" \
-X POST localhost:8000/api/admin/mail/test \
-H 'Content-Type: application/json' \
-d '{"to":"dein-postfach@example.de"}'
docker compose logs --tail=20 api
```
Für eine unabhängige Bewertung eignet sich ein Dienst, der dir eine
Wegwerf-Adresse gibt und die eingehende Mail auf SPF, DKIM, DMARC, PTR und
Inhaltsmerkmale prüft etwa `mail-tester.com`. Alles unterhalb von 8/10 lohnt
das Nachbessern.
Achte in der Auswertung besonders auf:
- `SPF: pass` mit der Envelope-Domain
- `DKIM: pass` mit `d=` gleich der From-Domain
- `DMARC: pass`
- kein Eintrag auf Spamhaus/Barracuda für deine Relay-IP
## Häufige Ursachen für Spam-Einstufung
| Symptom | Ursache |
|---|---|
| Landet bei Gmail im Spam, sonst nirgends | Fehlender oder falscher PTR-Eintrag |
| DKIM `pass`, DMARC `fail` | Signierende Domain weicht von der From-Domain ab |
| SPF `softfail` | Relay-IP nicht im SPF-Record der Envelope-Domain |
| Alles `pass`, trotzdem Spam | Neue Domain ohne Sendereputation das gibt sich nach einigen Wochen regelmäßigen Versands |
| Outlook/Hotmail blockt | Microsoft verlangt oft eine Anmeldung beim SNDS-Programm |