Deneyiminizi geliştirmek için çerezler kullanıyoruz. Bu siteyi ziyaret etmeye devam ederek çerez kullanımımızı kabul etmiş olursunuz.Gizlilik Politikası

EvilMailEvilMail
ÖzelliklerEvilMail ile yapabileceğiniz her şeyŞunlar için Geçici Mail…Discord, ChatGPT ve daha fazlası için geçici mailAğ AraçlarıDNS, WHOIS, port ve ağ araçlarıKarşılaştırEvilMail ile diğer geçici mail servislerinin karşılaştırması
APIREST API referansı ve rehberleriGeçici Mail APIHer dilde kopyala-yapıştır kod
UygulamalarFiyatlarBlog
HakkımızdaBiz kimiz ve neler geliştiriyoruzGüven ve GüvenlikVerilerinizi nasıl koruyoruzSSSSık sorulan soruların yanıtlarıİletişimEkibimizle iletişime geçin
GirişKayıt Ol
EvilMailEvilMail
UygulamalarFiyatlarBlog
GirişKayıt Ol
Ana SayfaBlogGelen E-postayı Webhook'a Çeviren Boru Hattı: MX'ten Uygulamanıza
Geliştiriciler

Gelen E-postayı Webhook'a Çeviren Boru Hattı: MX'ten Uygulamanıza

SendGrid Inbound Parse veya Mailgun Routes gibi bir servisten gelen-posta "kiralamak" kolaydır — ama o servis, postanıza dünyanın vereceği SMTP cevabını da sizin adınıza veriyor. Kendi Postfix + parser + imzalı webhook zincirinizi kurarak teslimat semantiğini geri alın: exit-code'dan SMTP cevabına eşleme, accept-then-queue omurgası ve MIME'ı gerçekten doğru ayrıştıran kod.

EvilMail Team
3 Ağustos 2026
11 dk okuma
Gelen E-postayı Webhook'a Çeviren Boru Hattı: MX'ten Uygulamanıza
İçindekiler
MX'i uygulamanıza değil, kabul eden bir MTA'ya yönlendirinPostfix'ten parser'a teslim: pipe mı, LMTP mıMIME'ı doğru ayrıştırmakWebhook payload'ını imzalayın ve idempotent yapınTeslimat garantileri: accept-then-queue omurgasıInbound kimlik doğrulama: SPF/DKIM/DMARC'ı payload'a taşıyınÜretim kontrol listesi

Gelen e-postayı uygulamanıza sokmanın en hızlı yolu bir kutucuğu işaretlemektir: SendGrid Inbound Parse, Mailgun Routes, Postmark inbound. MX kaydınızı onlara verirsiniz, onlar da JSON'u endpoint'inize POST eder. Faturada görünmeyen maliyet şu: o servis, postanızı göndermeye çalışan dünyanın geri kalanına verilen SMTP cevabını da sizin adınıza veriyor. Parser'ınız iki saniye düştüğünde gönderen ne görüyor — 250 OK mu, 451 try again mı, yoksa 550 rejected mı? Bu tek cevap, o mailin size ulaşıp ulaşmayacağını belirler; ve onu üçüncü bir tarafa kiraladınız.

Gelen-posta boru hattının gerçek zorluğu MIME ayrıştırmak değildir; MIME çözülmüş bir problemdir. Zorluk, SMTP konuşmasının hangi anında hangi cevabı vereceğinize karar vermektir. Yanlış yerde 5xx dönerseniz gönderen kalıcı bounce üretir ve mail sonsuza dek kaybolur. Yanlış yerde 250 dönüp sonra webhook'u düşürürseniz gönderen "teslim edildi" görür ama mesaj sizde buharlaşır — sessiz kayıp, en kötü hata sınıfı. Kendi Postfix + parser + imzalı webhook zincirinizi kurunca üç şeyi geri alırsınız: teslimat semantiğinin kontrolü, kişisel verinin üçüncü tarafın sunucularından geçmemesi ve payload şemasında sınırsız özgürlük.

Boru hattı: MX'ten webhook'a 250'i nerede döndüğünüz her şeyi belirler Gönderen MTA (uzaktaki sunucu) MX: Postfix kabul (postscreen + TLS) opendkim / rspamd → Auth-Results Dayanıklı kuyruk (spool) — burada 250 Parser: MIME → JSON, HMAC imzala HTTPS POST → Uygulama /webhook exit 0 → SMTP 250 (kabul, mesaj silinir) exit 75 → SMTP 4xx (kuyrukta kal, retry) 2xx → ack + dedup store 4xx/timeout → retry kuyruğu 60s · 5m · 30m · 2h · 6h 5xx / max deneme → dead-letter

MX'i uygulamanıza değil, kabul eden bir MTA'ya yönlendirin

MX kaydına bir HTTP endpoint yazamazsınız çünkü MX bir SMTP konuşması bekler: EHLO, MAIL FROM, RCPT TO, DATA. Gönderen MTA'lar retry'larını RFC 5321 zamanlamasıyla yapar, TLS pazarlığı ister, backscatter ve spam ilk kapıda elenmelidir. Bunların hiçbiri HTTP'de yok. Araya bir MTA koyarsınız ve onun tek işi kabul katmanı olmaktır: mesajı diske alır, 250 döner, geri kalan her şeyi asenkron yapar.

DNS tarafı sıradan ama ihmal edilirse acıtır:

dns
inbound.evilmail.pro.   IN  MX  10  mx1.evilmail.pro.
inbound.evilmail.pro.   IN  MX  20  mx2.evilmail.pro.
mx1.evilmail.pro.       IN  A       203.0.113.10

mx1/mx2 için A kaydı yetmez — PTR (reverse DNS) de olmalı, yoksa Google ve Microsoft dahil birçok gönderen daha DATA'ya gelmeden reddeder. İkinci bir MX (20) yedeklilik verir: mx1 bakımdayken gönderenler mx2'ye düşer, mail kaybolmaz. MTA-STS ve TLSA opsiyoneldir ama inbound TLS'i zorunlu kılmak istiyorsanız değerlidir.

Postfix'ten parser'a teslim: pipe mı, LMTP mı

Postfix'in içeri aldığı mesajı parser'ınıza vermesinin iki yolu var. İlki pipe transport'u: her mesaj için bir process fork edilir ve mesaj stdin'den akıtılır.

master.cf:

inbound   unix  -  n  n  -  10  pipe
  flags=DRhu user=inbound argv=/opt/inbound/handle.py ${sender} ${recipient} ${size}

main.cf ve transport eşlemesi:

message_size_limit = 26214400
transport_maps = hash:/etc/postfix/transport
# /etc/postfix/transport  →  postmap ile derleyin
@evilmail.pro   inbound:

İşin can alıcı yeri burada: pipe process'inin exit-code'u doğrudan SMTP cevabına eşlenir. sysexits.h sözlüğü Postfix'in sözlüğüdür:

  • EX_OK (0) → 250, mesaj teslim edildi kabul edilir ve kuyruktan silinir
  • EX_TEMPFAIL (75) → 4xx, Postfix mesajı kuyrukta tutar ve daha sonra tekrar dener
  • EX_NOUSER (67) / EX_NOPERM (77) → 5xx, gönderene kalıcı bounce üretilir
  • EX_DATAERR (65) → kalıcı reddet

Bu tablo boru hattının anayasasıdır. Parser'ınız veritabanına yazamıyorsa 75 döner ve Postfix'in dayanıklı kuyruğu sizin için retry mekanizması olur — kendi retry kodunuzu yazmadan önce bedava kurtarma katmanı. Ama asla, uygulamanız geçici olarak yanıt vermiyor diye 67/77 dönmeyin; o kalıcı kayıptır.

Ölçekte pipe çöker. 100+ eşzamanlı teslimde her mesaj için Python yorumlayıcısını fork etmek CPU'yu yer ve postqueue -p şişer. Çözüm LMTP: parser'ı long-running bir servis olarak çalıştırıp Postfix'i içine teslim ettirin.

inbound   unix  -  -  n  -  -  lmtp

transport: @evilmail.pro lmtp:inet:127.0.0.1:24. Fork maliyeti sıfırdır, concurrency'yi servis içinde kontrol edersiniz ve DB bağlantı havuzu persistent kalır. LMTP'de de exit semantiği korunur: parser 250/4xx/5xx'i LMTP protokolü üzerinden doğrudan döner. Küçük hacimde pipe ile başlayın, ilk trafik dalgasında LMTP'ye geçin.

MIME'ı doğru ayrıştırmak

Naif yaklaşımın kırıldığı tek satır, Python'ın hangi policy'yi kullandığınızdır. email modülü hâlâ geriye dönük uyumluluk için compat32 policy'sini varsayılan tutabilir ve o policy RFC 2047 ile kodlanmış unicode header'ları — Türkçe konu satırlarını, gönderen adlarını — bozar. Modern policy.default bunları otomatik çözer.

python
import sys
from email import policy
from email.parser import BytesParser

msg = BytesParser(policy=policy.default).parse(sys.stdin.buffer)

subject = msg["subject"]                       # RFC2047 otomatik decode
body = msg.get_body(preferencelist=("plain", "html"))
text = body.get_content() if body else ""      # charset otomatik çözülür

attachments = []
for part in msg.iter_attachments():
    data = part.get_payload(decode=True)       # bytes, base64/QP çözülmüş
    if data is None:
        continue
    attachments.append({
        "filename": part.get_filename(),
        "content_type": part.get_content_type(),
        "size": len(data),
        "payload": data,
    })

get_content() charset'i kendisi çözer; get_payload(decode=True) base64 ve quoted-printable'ı sizin için açar. Charset None geldiğinde utf-8 fallback'ini errors="replace" ile yapın, aksi halde tek bozuk byte tüm mesajı düşürür. iter_attachments(), inline cid: referanslarını gövdeden ayırt eder — HTML gövdesinde gömülü görselleri ek olarak saymazsınız. Bellek koruması için message_size_limit'i Postfix tarafında 25 MiB'ye çektik; parser içinde de decode edilmiş toplam boyutu sınırlayın, açıldığında yüzlerce megabayta şişen bir arşiv LMTP servisini OOM'a sokmasın.

Webhook payload'ını imzalayın ve idempotent yapın

Uygulamanız /webhook endpoint'ine gelen POST'un gerçekten sizin parser'ınızdan geldiğini bilmeli. HMAC-SHA256 ile ham gövdeyi imzalayın:

python
import hmac, hashlib

sig = hmac.new(SECRET, body_bytes, hashlib.sha256).hexdigest()
headers = {
    "X-EvilMail-Signature": f"sha256={sig}",
    "X-EvilMail-Delivery": message_id,
    "Content-Type": "application/json",
}

Uygulama tarafında mutlaka hmac.compare_digest() kullanın — normal == string karşılaştırması timing saldırısına açıktır. İmzayı ham gövde bytes'ı üzerinden hesaplayın; JSON'u parse edip yeniden serialize ederseniz byte'lar değişir ve imza tutmaz.

İkinci zorunluluk idempotency. Postfix bir mesajı retry sırasında birden fazla kez teslim edebilir — parser 75 döndükten sonra tekrar denendiğinde webhook ikinci kez ateşlenir. Dedup anahtarı olarak sha256(message_id + recipient) kullanın; uygulamanız bu anahtarı daha önce gördüyse işlemeden 200 döner. Bu olmadan bir kullanıcı tek maili iki fatura, iki bildirim olarak görür.

Payload şeması sabit ve öngörülebilir olsun:

json
{
  "message_id": "<[email protected]>",
  "from": "[email protected]",
  "to": "[email protected]",
  "subject": "Fatura sorusu",
  "date": "2026-07-04T10:22:11Z",
  "text": "...",
  "html": "...",
  "headers": { "...": "..." },
  "attachments": [
    { "filename": "fatura.pdf", "content_type": "application/pdf",
      "size": 84213, "url": "https://s3.../24h-signed" }
  ],
  "auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass" },
  "spam_score": -1.2
}

Ekleri inline base64 olarak gömmek payload'u şişirir ve 5 MB'lık PDF'i JSON içinde 6.67 MB'a çıkarır. Bunun yerine dosyayı geçici bir S3/MinIO bucket'ına koyup 24 saatlik imzalı URL verin. Uygulamanız gerçekten indirmek istediğinde çeker; çoğu zaman ek metadata'sı yeter.

Teslimat garantileri: accept-then-queue omurgası

Tek en önemli tasarım kararı: 250'i ne zaman dönersiniz? Cazip yanlış cevap "webhook 2xx döndükten sonra"dır — ama bu, gönderen MTA'yı sizin uygulamanızın latency'sine ve uptime'ına bağlar. Uygulamanız 30 saniye sürerse gönderen SMTP timeout'una girer ve mesajı tekrar dener; siz aynı maili üç kez alırsınız.

Doğru cevap: mesajı dayanıklı kuyruğa yazdıktan hemen sonra `250` dön, webhook'u asenkron dene. Birinci diyagramdaki mavi kutu — spool — 250'nin döndüğü yerdir. Postfix'in spool'u zaten fsync'lenmiş, güç kesintisine dayanıklı bir kuyruktur; ondan aldıktan sonra mesajı kaybetmezsiniz. Webhook başarısız olursa retry sizin iç kuyruğunuzun işidir, gönderenin değil.

Retry programı üstel geri çekilmeyle: 60s, 300s, 1800s, 7200s, 21600s, sonra dead-letter tablosu ve operatör alarmı. Webhook için 10s timeout uygulayın ve sadece 2xx'i başarı sayın. Kural nettir: 5xx = kalıcı fail, dead-letter'a git; 4xx ve timeout = retry. 3xx redirect'leri takip etmeyin — webhook endpoint'i yönlendirme yapmamalı.

Postfix'in kendi kuyruk ömrünü de ayarlayın ki mesajlar sonsuza dek asılı kalmasın:

maximal_queue_lifetime = 5d
bounce_queue_lifetime = 5d
Exit-code → SMTP cevabı → gönderen davranışı Bu üç sütun yazının tüm tezidir: SMTP cevabı asıl mesele PARSER EXIT SMTP CEVABI GÖNDEREN NE YAPAR exit 0 EX_OK 250 Teslim edildi mesaj kuyruktan silinir exit 75 EX_TEMPFAIL 4xx Dakikalar/saatler sonra RETRY mesaj gönderende kuyrukta kalır exit 67 / 77 NOUSER / NOPERM 5xx Kalıcı BOUNCE üretir bir daha asla denemez Yanlış yerde 5xx = kalıcı mail kaybı · yanlış yerde 250 + webhook fail = sessiz kayıp

Inbound kimlik doğrulama: SPF/DKIM/DMARC'ı payload'a taşıyın

Gelen mailin from başlığına güvenip aksiyon almak — "bu adres CEO'muzunki, ödemeyi onayla" — spoofing'e açık kapıdır. From: başlığı serbest metindir, kimse doğrulamaz. Doğrulanan şey SPF (zarf gönderen IP'si), DKIM (imza) ve DMARC (ikisinin hizası) sonuçlarıdır.

En temiz kurulum: opendkim veya rspamd'yi Postfix'in önünde çalıştırın; bunlar mesaja Authentication-Results: başlığı ekler. Parser bu başlığı authres ile ayrıştırıp sonucu payload'a koyar. Forwarding'e karşı ekstra güven istiyorsanız parser içinde dkimpy ile imzayı yeniden doğrulayabilirsiniz, ama pratikte MTA önündeki doğrulama yeterli ve daha hızlıdır.

Payload'a "auth": {"spf": "pass", "dkim": "pass", "dmarc": "pass"} koyun ve kararı uygulamaya bırakın. Altın kural: `dmarc != pass` ise uygulama otomatik aksiyon almasın — sadece kaydetsin. Bir insana veya moderasyon kuyruğuna düşsün. Forwarding senaryolarında (mailing list, alias) SPF kırılır ama DKIM ve varsa ARC zinciri korunur; bu yüzden bir mail forward edilmişse ARC başlıklarını da payload'a dahil edip uygulamanın "bu zincirin bir noktasında doğrulanmış" bilgisine erişmesini sağlayın.

Üretim kontrol listesi

  • PTR + A + MX üçlüsünün ikisi de (mx1, mx2) tam ve tutarlı — reverse DNS eksikse gönderenler RCPT'te reddeder.
  • Parser user=inbound unprivileged çalışır; systemd sandbox ile sıkın: NoNewPrivileges=yes, ProtectSystem=strict, PrivateTmp=yes.
  • Exit-code sözlüğü kodda sabit: DB hatası → 75, bilinmeyen alıcı → 67, geçici hatada asla 5xx yok.
  • 250'yi yalnızca dayanıklı kuyruğa yazdıktan sonra dönün — webhook 2xx'ini beklemeyin.

Bu zinciri kurduğunuzda gelen-posta artık üçüncü bir tarafın uptime'ına ve iyi niyetine bağlı bir kara kutu değil; SMTP cevabından webhook imzasına kadar her baytı sizin denetlediğiniz bir boru hattıdır. İlk trafik dalgası geldiğinde farkı, mail kaybetmeyerek anlarsınız.

Paylaş
𝕏 TwitterLinkedInReddit
ÖncekiDKIM'i Uygulamada mı MTA'da mı İmzalamalı: Mimari Kararın TuzaklarıSonrakiCI/CD Hattında E-postaları Mailpit ile Yakalayıp Otomatik Test Etmek

İlgili Yazılar

Geliştiriciler

ManageSieve ile Sieve Script'lerini Uzaktan Yönetmek: Merkezi Dağıtım ve Sürümleme

11 dk okuma
Geliştiriciler

SMTP DSN ile Teslim ve Gecikme Bildirimlerini Programatik İşlemek

13 dk okuma
Geliştiriciler

CI/CD Hattında E-postaları Mailpit ile Yakalayıp Otomatik Test Etmek

11 dk okuma
EvilMailEvilMail

EvilMail — ücretsiz temp mail ve geçici e-posta servisi. Kayıt gerektirmez.

Ürün

ÖzelliklerFiyatlarAPITools

Kaynaklar

BlogFAQGlossaryKayıt OlHakkımızda

Yasal

Gizlilik PolitikasıKullanım KoşullarıGüven ve Güvenlikİletişim
© 2026 EvilMail. Tüm hakları saklıdır.
Webhook: HMAC-SHA256 imza + compare_digest, 10s timeout, sadece 2xx başarı, sha256(message_id+recipient) ile idempotency.
  • Retry 60s·5m·30m·2h·6h → dead-letter; dead-letter tablosuna alarm bağlı.
  • Ekler geçici storage'a + 24h imzalı URL; JSON'a base64 gömme.
  • postqueue -p izlenir; kuyruk birikiyorsa parser darboğazı — pipe'tan LMTP'ye geç.
  • Gönderen IP başına RCPT rate limit (postscreen veya cidr tablosu); bir IP'nin catch-all'ı taramasını engelle.
  • dmarc != pass mailler otomatik işlenmez, moderasyona düşer.