Przejdź do treści
correct.email
Dla programistów

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 4yz przekaźnika SMTP — RFC 5321 traktuje 4yz jako przejściowe, a 5yz jako 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ństwanie — nie ma się z czego wypisać
Potwierdzenie zakupu, faktura, status zamówienianie
Opcjonalne powiadomienia (podsumowania, raporty tygodniowe)tak — wyłącza tylko to powiadomienie
Newslettery, ogłoszeniatak, 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.status ma 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:

  1. spf=pass dla Twojej subdomeny odbić, dkim=pass z header.d w Twojej domenie, dmarc=pass dla domeny From.
  2. Return-Path w Twojej subdomenie odbić, nie w domenie dostawcy.
  3. Jeden nagłówek From, nagłówek Date i unikalny Message-ID.
  4. Brak List-Unsubscribe w resetach haseł i potwierdzeniach zakupów; działający w opcjonalnych powiadomieniach.
  5. TLS na odcinku do serwera odbiorcy — with ESMTPS w jego nagłówku Received.

Źródła