Poczta transakcyjna z aplikacji: jak ją wysyłać
Jak wysyłać z aplikacji e-maile, które trafiają do skrzynki: osobna subdomena, zgodne SPF, DKIM i DMARC, obsługa odbić i skarg, bezpieczne ponowienia i testy.
Autor: Exmoor Software7 min czytania
Poczta transakcyjna to wiadomości, które aplikacja wysyła, bo użytkownik coś zrobił: potwierdzenie rejestracji, reset hasła, kod logowania, potwierdzenie zakupu. Użytkownicy oczekują ich w ciągu kilku sekund, a gdy wiadomość trafia do spamu, piszą do pomocy technicznej — albo rezygnują.
O niezawodności decyduje głównie to, co konfiguruje się raz: domena nadawcza, uwierzytelnianie zgodne z tą domeną i kod, który obsługuje błędy bez wysyłania duplikatów. Wszystko poniżej dotyczy każdego dostawcy — API do wysyłki e-maili, przekaźnika SMTP czy własnego serwera poczty; różnią się tylko nazwy ustawień.
1. Osobna subdomena dla poczty z aplikacji
Wysyłaj pocztę aplikacji z osobnej subdomeny, np. notify.example.com, marketing — z innej, np. news.example.com, a example.com zostaw dla skrzynek prawdziwych osób.
From: Example <account@notify.example.com> ← resety haseł, potwierdzenia zakupów
From: Example News <hello@news.example.com> ← newslettery, promocje
From: Anna Nowak <anna@example.com> ← ludzie
- Osobne rekordy DNS. Każda subdomena ma własny rekord SPF, własne klucze DKIM i — jeśli chcesz — politykę DMARC. Zmiana narzędzia marketingowego nie dotyka rekordów, od których zależą e-maile z kodami logowania.
- Częściowo osobna reputacja. Dostawcy skrzynek mogą oceniać subdomenę osobno, więc kampania, na którą ludzie się skarżą, rzadziej zaszkodzi resetom haseł. To jednak nie szczelna przegroda: subdomeny mają wspólną domenę organizacyjną, a Gmail na przykład wlicza wiadomości z tej samej domeny głównej łącznie do progu 5000 wiadomości dziennie, od którego traktuje nadawcę jako masowego.
- Czytelny podział. Filtry, odbiorcy i Twoje logi łatwo odróżnią oba rodzaje poczty.
Nie przenoś poczty transakcyjnej na świeżo zarejestrowaną domenę w rodzaju example-mail.com: nie ma ona reputacji i przyzwyczaja użytkowników do ufania domenom łudząco podobnym do prawdziwej.
2. Uwierzytelniaj własną domeną, nie domeną dostawcy
DMARC daje wynik pozytywny, gdy SPF albo DKIM przejdzie dla domeny zgodnej z domeną From — w domyślnym trybie swobodnym (relaxed) wystarczy ta sama domena organizacyjna (zgodność domen DMARC). W ustawieniach domyślnych wielu dostawców uwierzytelnia wiadomości swoją domeną, a nie Twoją: SPF i DKIM przechodzą, DMARC nie. Naprawiają to trzy ustawienia.
Własny DKIM
Niech dostawca podpisuje wiadomości z d=notify.example.com (albo d=example.com). Zwykle publikujesz rekord CNAME, który kieruje selektor na klucz utrzymywany przez dostawcę — wtedy rotację kluczy może przeprowadzać sam dostawca:
s1._domainkey.notify.example.com. 3600 IN CNAME s1.dkim.esp.example.
Niektórzy dostawcy dają zamiast tego rekord TXT z kluczem publicznym. Sprawdź efekt narzędziem Sprawdzanie DKIM: typowy wybór to RSA 2048 bitów, a serwery weryfikujące podpisy muszą odrzucać klucze krótsze niż 1024 bity (RFC 8301). To najważniejsze z trzech ustawień, bo DKIM przetrwa przekazanie wiadomości dalej, a SPF nie.
Własny Return-Path (domena odbić)
SPF sprawdza nadawcę z koperty — adres z polecenia MAIL FROM w sesji SMTP, zapisywany przez odbiorcę jako Return-Path — a nie nagłówek From. Jeśli dostawca używa własnej domeny odbić, SPF przechodzi dla domeny dostawcy, ale nie jest zgodny z domeną From. Skonfiguruj własną subdomenę odbić:
bounce.notify.example.com. 3600 IN CNAME bounces.esp.example.
Dzięki CNAME rekord SPF tej nazwy (i rekord MX, jeśli dostawca go potrzebuje) utrzymuje dostawca i to do niego trafiają odbicia. bounce.notify.example.com ma wspólną domenę organizacyjną example.com z domeną From, więc SPF jest zgodny w trybie swobodnym. CNAME nie może współistnieć z innymi rekordami pod tą samą nazwą, więc użyj nazwy przeznaczonej tylko do tego.
Include w SPF — tylko tam, gdzie SPF jest sprawdzany
Część dostawców nie korzysta z CNAME, tylko prosi o dopisanie ich include: do rekordu SPF domeny, której używają jako nadawcy z koperty:
bounce.notify.example.com. 3600 IN TXT "v=spf1 include:spf.esp.example -all"
Dopisz go do rekordu tej domeny, która faktycznie pojawia się w Return-Path. Każda nazwa ma własny rekord SPF: include dodany „na wszelki wypadek” do example.com nic nie daje poczcie, której nadawcą z koperty jest subdomena, a i tak wlicza się do limitu 10 zapytań DNS tego rekordu (SPF: za dużo zapytań DNS). Liczbę zapytań pokaże Sprawdzanie SPF.
DMARC
Jeśli notify.example.com nie ma własnego rekordu DMARC, odbiorcy stosują rekord example.com — jego tag sp, a gdy go nie ma, p. Możesz też opublikować rekord dla subdomeny:
_dmarc.notify.example.com. 3600 IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@example.com"
Jeśli nie masz pewności, że wszystkie źródła wysyłające pocztę z tej subdomeny są zgodne, zacznij od p=none, czytaj raporty zbiorcze przez kilka tygodni, potem przejdź na p=quarantine lub p=reject (jak skonfigurować DMARC). Subdomenę używaną tylko przez aplikację często najłatwiej doprowadzić do p=reject, bo dokładnie wiesz, co z niej wysyła. Sprawdzisz ją narzędziem Sprawdzanie DMARC.
3. Obsługa odbić i skarg
Wiadomość, której nie da się doręczyć, zostaje odrzucona jeszcze w sesji SMTP albo wraca jako odbicie — powiadomienie o stanie doręczenia (DSN, RFC 3464) wysłane na adres z Return-Path. Dostawca odbiera odbicia w domenie odbić i przekazuje je Tobie, zwykle jako zdarzenia webhooka. Skargi (kliknięcia „zgłoś spam”) przychodzą przez pętle zwrotne (feedback loops) tych dostawców skrzynek, którzy je udostępniają, często w formacie ARF (RFC 5965); Gmail zamiast tego pokazuje zbiorcze wskaźniki skarg w Postmaster Tools.
- Najpierw weryfikacja i zapis, potem przetwarzanie. Sprawdź podpis webhooka tak, jak opisuje to dostawca, zapisz surowe zdarzenie, szybko odpowiedz kodem 2xx i przetwarzaj z kolejki. Dostawcy ponawiają webhooki, więc licz się z duplikatami i zdarzeniami w innej kolejności; usuwaj duplikaty po identyfikatorze zdarzenia nadanym przez dostawcę.
- Odbicia trwałe (hard bounce), np. rozszerzony kod statusu
5.1.1(skrzynka nie istnieje): dopisz adres do listy wykluczeń (suppression list) i przestań na niego wysyłać. Poproś użytkownika w aplikacji o poprawienie adresu. Zobacz kody odrzuceń poczty. - Odbicia przejściowe (soft bounce,
4.x.x), np. pełna skrzynka: dostawca ponawia wysyłkę. Jeśli adres odbija się tak przez kilka dni, traktuj go jak odbicie trwałe dla poczty, która nie jest niezbędna. - Skargi: przestań wysyłać na ten adres pocztę opcjonalną. Prowadź listy wykluczeń osobno dla każdego strumienia — skarga na newsletter nie powinna blokować jutrzejszego resetu hasła, a odbicie trwałe dotyczy wszystkich strumieni.
- Sprawdzaj listę wykluczeń we własnym kodzie, zanim wywołasz dostawcę — wtedy wiesz, dlaczego wiadomość nie wyszła.
4. Wysyłka idempotentna i bezpieczne ponowienia
Większość incydentów to wiadomości, które nie wyszły, bo coś przerwało się w połowie, albo wyszły dwa razy, bo coś ponowiło próbę.
- Nie wysyłaj bezpośrednio w obsłudze żądania HTTP. Zapisz zamiar wysyłki w tej samej transakcji bazy danych co zdarzenie biznesowe (wiersz „outbox”: zamówienie 1042 opłacone → wyślij potwierdzenie) i niech dostarczy go proces w tle. Wycofana transakcja niczego nie wysyła; gdy proces padnie, wiersz dalej czeka.
- Używaj klucza idempotencji wyprowadzonego ze zdarzenia, np.
receipt:order-1042. Przekaż go dostawcy, jeśli jego API to obsługuje, i wymuś go unikalnym indeksem w swojej tabeli wysłanych wiadomości. - Ponawiaj tylko błędy przejściowe: przekroczenie czasu, błędy połączenia, HTTP 429 i 5xx albo odpowiedzi
4yzprzekaźnika SMTP — RFC 5321 traktuje4yzjako przejściowe, a5yzjako trwałe. Stosuj wykładnicze odstępy z losowym rozrzutem (jitter) i limit prób. - Traktuj przekroczenie czasu jako „może wysłane”. Dostawca mógł przyjąć wiadomość tuż przed zerwaniem połączenia — dokładnie ten przypadek obsługuje klucz idempotencji.
- Pilnuj ważności. Kod logowania, który dotrze po wygaśnięciu, jest gorszy niż żaden; porzuć zadanie i pozwól użytkownikowi poprosić o nowy.
- Przechowuj identyfikatory. Zapisuj generowany
Message-ID(unikalny dla każdej wiadomości, RFC 5322) i identyfikator wiadomości u dostawcy, żeby dopasować zdarzenia z webhooków.
5. Nie mieszaj poczty transakcyjnej z marketingową
Potwierdzenie zakupu z blokiem promocyjnym to dla filtrów i wielu odbiorców wiadomość marketingowa. Wytyczne Google mówią, żeby nie mieszać różnych rodzajów treści w jednej wiadomości (żadnych promocji w potwierdzeniach zakupu), wiadomości tej samej kategorii wysyłać z tego samego adresu From, a przy wysyłce z kilku adresów IP używać osobnego IP dla każdego rodzaju wiadomości. Utrzymuj więc osobne subdomeny, osobne strumienie lub pule IP, jeśli dostawca je oferuje, osobne selektory DKIM i osobne listy wykluczeń.
6. List-Unsubscribe tylko tam, gdzie pasuje
Gmail i Yahoo wymagają od nadawców masowych wypisania jednym kliknięciem (RFC 8058) w wiadomościach marketingowych i subskrypcyjnych; FAQ Google mówi, że wiadomości transakcyjne są z tego wyłączone.
| Wiadomość | List-Unsubscribe |
|---|---|
| Reset hasła, kod logowania, alert bezpieczeństwa | nie — nie ma się z czego wypisać |
| Potwierdzenie zakupu, faktura, status zamówienia | nie |
| Opcjonalne powiadomienia (podsumowania, raporty tygodniowe) | tak — wyłącza tylko to powiadomienie |
| Newslettery, ogłoszenia | tak, jednym kliknięciem; wysyłane strumieniem marketingowym |
Szczegóły: wypisanie jednym kliknięciem. Rozważ też nagłówek Auto-Submitted: auto-generated (RFC 3834): oznacza wiadomość jako wygenerowaną automatycznie, a autorespondery, np. odpowiedzi „jestem poza biurem”, nie powinny na nią reagować. Jeśli użytkownicy mogą odpisać, ustaw Reply-To na adres, który ktoś czyta.
7. Testy na stagingu i analiza nagłówków
- Środowisko deweloperskie i testy automatyczne: lokalny „łapacz” poczty albo tryb sandbox dostawcy, żeby testy nigdy nie trafiały do prawdziwych klientów.
- Staging: wysyłka przez prawdziwego dostawcę, z produkcyjną subdomeną albo identycznie skonfigurowaną, na testowe skrzynki u głównych dostawców skrzynek, do których masz dostęp.
- W procesie wdrożenia: po zmianach w DNS sprawdzaj SPF, DKIM i DMARC subdomeny nadawczej przez API correct.email i przerywaj wdrożenie, gdy
result.statusma wartośćfail. - Po każdej zmianie w DNS lub u dostawcy: wklej pełne nagłówki odebranej wiadomości do analizatora nagłówków e-mail. Działa w całości w przeglądarce i pokazuje trasę wiadomości oraz werdykty zapisane przez odbiorcę. Jak czytać je samodzielnie: jak czytać nagłówki e-mail.
Poprawnie skonfigurowana wiadomość wygląda tak:
Return-Path: <b-8f2c1e@bounce.notify.example.com>
Authentication-Results: mx.example.net;
spf=pass smtp.mailfrom=bounce.notify.example.com;
dkim=pass header.d=notify.example.com header.s=s1;
dmarc=pass header.from=notify.example.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=notify.example.com; s=s1;
h=from:to:subject:date:message-id; bh=…; b=…
From: Example <account@notify.example.com>
Message-ID: <20261023091402.7f3a91@notify.example.com>
Auto-Submitted: auto-generated
Sprawdź w każdej wiadomości testowej:
spf=passdla Twojej subdomeny odbić,dkim=passzheader.dw Twojej domenie,dmarc=passdla domeny From.Return-Pathw Twojej subdomenie odbić, nie w domenie dostawcy.- Jeden nagłówek From, nagłówek Date i unikalny Message-ID.
- Brak List-Unsubscribe w resetach haseł i potwierdzeniach zakupów; działający w opcjonalnych powiadomieniach.
- TLS na odcinku do serwera odbiorcy —
with ESMTPSw jego nagłówku Received.
Źródła
- RFC 5321 — Simple Mail Transfer Protocol
- RFC 5322 — Internet Message Format
- RFC 7208 — SPF
- RFC 6376 — DKIM Signatures
- RFC 8301 — Cryptographic Algorithm and Key Usage Update to DKIM
- RFC 9989 — DMARC
- RFC 3464 — Delivery Status Notifications
- RFC 5965 — An Extensible Format for Email Feedback Reports
- RFC 3834 — Recommendations for Automatic Responses to Electronic Mail
- RFC 8058 — sygnalizowanie wypisania jednym kliknięciem w nagłówkach list
- Google — wytyczne dla nadawców e-maili
- Google — FAQ do wytycznych dla nadawców
- Yahoo — dobre praktyki dla nadawców