Wszystkie artykuły
Lokalne testowanie webhooków Postmark przez HTTPS
Postmarkemail webhookslocalhostwebhook security

Lokalne testowanie webhooków Postmark przez HTTPS

Aby przetestować webhook Postmark na localhost, uruchom lokalny endpoint, udostępnij jego port poleceniem npx portpreview PORT i wpisz uzyskany adres HTTPS we właściwym Postmark Message Stream. Zabezpiecz trasę przez Basic Authentication albo własny tajny nagłówek, waliduj każdy payload JSON, zapisuj go idempotentnie i szybko zwracaj HTTP 200.

Co Postmark wysyła do webhooka

Po zdarzeniu e-mailowym Postmark wykonuje HTTP POST. Wychodzący Message Stream raportuje dostarczenie, odbicie, otwarcie, kliknięcie, skargę na spam i zmianę subskrypcji, a strumień przychodzący przesyła sparsowaną wiadomość. Pola zależą od zdarzenia, dlatego rozdzielaj je po RecordType i sprawdzaj właściwy schemat. Dostarczenie oznacza przyjęcie przez serwer odbiorcy, nie obecność w skrzynce. Odbicie zawiera m.in. Type, TypeCode, Inactive i CanActivate. Szczegóły opisują oficjalny przegląd webhooków Postmark oraz dokumentacja webhooka odbić.

Zbuduj mały lokalny odbiornik Express

Przykład na porcie 3000 sprawdza Basic Auth przed JSON-em, waliduje minimalną kopertę, trwale zapisuje klucz deduplikacji i dopiero wtedy potwierdza. Funkcje bazy zastąp własną transakcją lub kolejką.

import crypto from 'node:crypto';
import express from 'express';

const app = express();
app.use('/webhooks/postmark', express.json({ limit: '2mb' }));

function safeEqual(actual, expected) {
  const a = Buffer.from(actual);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function authorized(req) {
  const value = req.get('authorization') ?? '';
  if (!value.startsWith('Basic ')) return false;
  const decoded = Buffer.from(value.slice(6), 'base64').toString('utf8');
  const separator = decoded.indexOf(':');
  if (separator < 0) return false;
  return safeEqual(decoded.slice(0, separator), process.env.POSTMARK_WEBHOOK_USER ?? '') &&
    safeEqual(decoded.slice(separator + 1), process.env.POSTMARK_WEBHOOK_PASSWORD ?? '');
}

app.post('/webhooks/postmark', async (req, res) => {
  if (!authorized(req)) return res.sendStatus(401);

  const event = req.body;
  if (typeof event?.RecordType !== 'string' ||
      typeof event?.MessageID !== 'string') {
    return res.status(400).json({ error: 'Invalid Postmark event' });
  }

  const deliveryKey = `${event.RecordType}:${event.MessageID}:${event.ID ?? ''}`;
  await saveWebhookOnce({ provider: 'postmark', deliveryKey, event });
  res.sendStatus(200);
});

app.listen(3000);

Przy wrażliwych akcjach stosuj ścisłą listę pól i limit rozmiaru żądania. Nie loguj całych wiadomości przychodzących — mogą zawierać dane osobowe, linki logowania, załączniki i poufną korespondencję.

Udostępnij localhost pod publicznym adresem HTTPS

  1. Uruchom odbiornik i sprawdź lokalny port.
  2. W drugim terminalu wykonaj npx portpreview 3000.
  3. Do adresu HTTPS dodaj trasę, np. https://YOUR-TUNNEL.portpreview.dev/webhooks/postmark.
  4. Pozostaw tunel aktywny; po zmianie adresu zaktualizuj Postmark.

Tunel zapewnia osiągalność i zaufany HTTPS, lecz nie uwierzytelnia Postmark. Każdy znający URL może wysłać POST, więc nadal potrzebujesz autoryzacji i walidacji. Przed użyciem danych klientów przeczytaj poradnik bezpieczeństwa tunelu localhost.

Skonfiguruj właściwy webhook Postmark

Dla zdarzeń wychodzących wybierz w Postmark odpowiedni Server i Message Stream, otwórz Webhooks, dodaj URL i włącz tylko obsługiwane wyzwalacze. Dla poczty przychodzącej ustaw osobny inbound URL w Inbound Message Stream. Zespoły konfigurujące hooki kodem mogą użyć oficjalnego Webhooks API: obiekt obsługuje HttpAuth, opcjonalne HttpHeaders i ustawienia wyzwalaczy. X-Postmark-Server-Token służy do wywołań API, a nie jako sekret przesyłany do odbiornika.

Uwierzytelnianie Postmark nie jest podpisem kryptograficznym

Postmark obecnie nie obsługuje weryfikacji podpisu HMAC webhooków. Nie istnieje sekret podpisujący do odtworzenia skrótu surowego body ani nagłówek X-Postmark-Signature. Dokumentowane zabezpieczenia to HTTP Basic Authentication, lista dozwolonych IP i własne nagłówki. Basic Auth lub tajny nagłówek potwierdza znajomość wspólnego sekretu, ale nie wiąże kryptograficznie sekretu z body. HTTPS chroni go w transmisji; później nadal waliduj payload. Zakresy IP pobieraj z aktualnej dokumentacji, bo adres źródłowy może zmieniać się między próbami.

Preferuj pola HttpAuth zamiast danych w URL. Jeśli używasz https://username:[email protected]/path, generuj osobne silne dane, poprawnie koduj znaki i chroń URL przed logami. Nigdy nie używaj tokenu API Server jako hasła webhooka.

Obsługuj dostarczenia i odbicia według typu

Logikę biznesową przenieś poza żądanie HTTP; worker może przetwarzać trwałe zdarzenia idempotentnie:

async function processPostmarkEvent(event) {
  switch (event.RecordType) {
    case 'Delivery':
      await markAcceptedByRecipientServer({
        messageId: event.MessageID,
        deliveredAt: event.DeliveredAt
      });
      break;
    case 'Bounce':
      await recordBounce({
        bounceId: String(event.ID),
        messageId: event.MessageID,
        type: event.Type,
        inactive: event.Inactive,
        canActivate: event.CanActivate
      });
      break;
    default:
      await recordUnhandledPostmarkType(event.RecordType);
  }
}

Nie ustalaj trwałej blokady wyłącznie po nazwie pola; stosuj bieżącą klasyfikację Postmark i własną politykę. Skargi i zmiany subskrypcji mają osobne typy. Otwarcia i kliknięcia mogą się powtarzać.

Zaprojektuj obsługę ponowień i duplikatów

Postmark ponawia żądanie bez HTTP 200; odbicia i inbound mają dłuższy harmonogram niż kliknięcia, otwarcia, dostarczenia i zmiany subskrypcji, a 403 zatrzymuje próby. Timeout po commitcie może wywołać poprawny duplikat. Użyj unikalnego stabilnego klucza: Postmark zaleca MessageID, a przy wielu typach dodaj RecordType i identyfikator zdarzenia, np. ID. Zwracaj 200 dopiero po minimalnym trwałym przekazaniu, ale nie czekaj na zewnętrzne API. Wzorce opisuje poradnik ponowień i idempotencji webhooków.

Bezpiecznie testuj prawdziwą obsługę zdarzeń

Najpierw wyślij curl-em syntetyczny POST i sprawdź routing, uwierzytelnienie, walidację oraz zapis. Potem wygeneruj prawdziwe zdarzenia: dostarczenie na własny adres, a odbicie przez udokumentowane narzędzia Postmark, w tym domenę black-hole, jeśli jest dostępna. Zachowaj MessageID, wyślij oczyszczony fixture dwukrotnie i sprawdź pojedynczy efekt. Awarię przejściową symuluj tylko kontrolowanie.

Diagnozuj typowe awarie webhooków Postmark

Żądanie nie dociera do localhost

Sprawdź proces tunelu, pełną trasę URL i zgodność portu. Strona tunelu potwierdza edge, nie trasę POST.

Każde żądanie zwraca 401

Porównaj dane z lokalnym środowiskiem, zrestartuj aplikację i sprawdź, czy proxy nie usuwa Authorization. Nie loguj wartości. Pomaga poradnik błędów webhook 401/403.

Postmark ponawia po udanym przetworzeniu

Sprawdź publiczny status i opóźnienie: Postmark oczekuje dokładnie 200. Nie dopuść, by błąd po zapisie dał 500; zapis musi być idempotentny.

Payload różni się od przykładu

Sprawdź RecordType, wyzwalacz oraz typ strumienia. Odrzucaj brak wymaganych pól, toleruj udokumentowane dodatki i aktualizuj fixtures według referencji.

Lista bezpieczeństwa na produkcję

  • Używaj HTTPS i osobnych silnych danych Basic Auth lub tajnego nagłówka.
  • Rozdziel tokeny API, dane webhooka i sekrety produkcyjne.
  • Po testach rotuj dane i usuwaj stare URL-e.
  • Waliduj content type, rozmiar, typ, identyfikatory i pola.
  • Redaguj dane e-mail i sekrety w logach.
  • Stosuj najmniejsze uprawnienia.
  • Monitoruj błędy autoryzacji, opóźnienie, duplikaty i dead letters.

Dobry test lokalny odtwarza produkcję: uwierzytelniony HTTPS, ścisła walidacja, trwała idempotencja, szybkie HTTP 200 i asynchroniczna praca. Szerszy proces znajdziesz w poradniku lokalnego debugowania webhooków.

Najczęściej zadawane pytania

Jak przetestować webhook Postmark na localhost?
Uruchom lokalny endpoint, udostępnij port przez npx portpreview PORT, dodaj trasę do wygenerowanego URL HTTPS i wpisz go we właściwym Postmark Message Stream.
Czy Postmark podpisuje webhooki przez HMAC?
Nie. Aktualna dokumentacja nie przewiduje podpisów HMAC. Użyj HTTPS z Basic Authentication, opcjonalnie bieżącej listy IP Postmark, i waliduj każdy payload.
Dlaczego Postmark ponownie wysyła ten sam webhook?
Postmark ponawia bez HTTP 200, więc timeout lub błędna odpowiedź może powtórzyć już przetworzone zdarzenie. Zapisuj stabilny klucz z unikalnym ograniczeniem.
Czy webhook dostarczenia oznacza przeczytanie wiadomości?
Nie. Dostarczenie oznacza przyjęcie e-maila przez serwer odbiorcy, a nie umieszczenie w skrzynce, otwarcie ani przeczytanie.