Bounce Mail Analizi: Teslimat Hatalarını Okuma ve Çözme
Bir gün patronunuz size gelip “Müşterilere gönderdiğimiz kampanya mailleri gitmiyor, ne olduğunu bul” dediğinde, elinizdeki tek ipucu genellikle bir bounce mailidir. O küçük, teknik dolu mesaj aslında size çok şey anlatır, yeter ki nasıl okuyacağınızı bilin. Mail teslimat sorunları sysadmin hayatının en sinir bozucu kısımlarından biridir çünkü sorun bazen sizin sunucunuzda, bazen karşı tarafta, bazen DNS’te, bazen de tamamen rastgele görünen bir kara listede olur. Bu yazıda bounce mail analizini başından sonuna kadar ele alacağız.
Bounce Mail Nedir ve Neden Önemlidir
Bounce mail, göndericiye iletilen teslimat başarısızlık bildirimidir. Teknik adıyla NDR (Non-Delivery Report) veya DSN (Delivery Status Notification) olarak geçer. SMTP protokolünün bir parçası olup RFC 3464 standardında tanımlanmıştır.
Bounce mailleri iki ana kategoriye ayrılır:
- Hard Bounce: Kalıcı teslimat hatası. Adres yok, domain yok, hesap kapatılmış. Tekrar denemenin anlamı yok.
- Soft Bounce: Geçici teslimat hatası. Posta kutusu dolu, sunucu geçici olarak erişilemez, bant genişliği sınırı aşıldı. Belirli süre sonra tekrar denenebilir.
Neden önemli? Çünkü bounce analizi yapmadan mail altyapınızın gerçek durumunu bilemezsiniz. Yüzde kaçı gidiyor, neden gitmiyor, hangi domain’ler sorunlu, IP’niz kara listede mi? Bunların hepsini bounce mailleri size söyler.
Bounce Mailin Anatomisi
Bir bounce mail genellikle üç bölümden oluşur: insan tarafından okunabilir açıklama, orijinal mesaj başlıkları ve makine tarafından okunabilir DSN bölümü. Postfix kullanan bir sistemden gelen örnek bounce’a bakalım:
# Bir bounce mailin ham içeriğini görmek için
cat /var/mail/admin | grep -A 100 "Return-Path: <>"
Tipik bir bounce mailin yapısı şöyle görünür:
From: Mail Delivery Subsystem <[email protected]>
To: [email protected]
Subject: Undeliverable: Toplanti daveti
This is the mail system at host mail.ornekdomain.com.
Your message could not be delivered to one or more recipients.
<[email protected]>: host mail.karsialan.com[192.168.1.10] said:
550 5.1.1 The email account that you tried to reach does not exist.
Please try double-checking the recipient's email address for typos or
unnecessary spaces. (in reply to RCPT TO command)
------ This is a copy of the message, including all the headers. ------
Bu mesajda dikkat etmeniz gereken kısımlar şunlar:
- 550: SMTP yanıt kodu
- 5.1.1: Detaylı hata kodu (Enhanced Status Code)
- Hata mesajının hangi SMTP komutuna yanıt olarak geldiği
SMTP Hata Kodlarını Anlamak
SMTP hata kodları üç haneli sayılardır ve her rakam bir anlam taşır. İlk rakam sonucu belirtir:
- 2xx: Başarılı
- 4xx: Geçici hata, tekrar dene
- 5xx: Kalıcı hata, tekrar deneme
En sık karşılaşılan kodlar:
- 421: Servis geçici olarak kullanılamıyor
- 450: Posta kutusu geçici olarak erişilemez
- 451: İşlem iptal edildi, yerel hata
- 452: Depolama alanı yetersiz
- 500: Sözdizimi hatası
- 501: Parametre sözdizimi hatası
- 503: Hatalı komut sırası
- 550: Posta kutusu bulunamadı veya erişilemez
- 551: Kullanıcı bu sunucuda değil
- 552: Depolama kotası aşıldı
- 553: Geçersiz adres formatı
- 554: İşlem başarısız, spam veya güvenlik politikası
Enhanced Status Code’lar ise X.Y.Z formatındadır ve daha fazla detay verir. Örneğin 5.7.1 “mesaj reddedildi, güvenlik politikası” anlamına gelir.
Postfix Log Analizi
Bounce mailleri anlamak için sadece bounce mesajına bakmak yetmez, sunucu loglarına da bakmanız gerekir. Postfix’te mail logları genellikle /var/log/mail.log veya /var/log/maillog dosyasındadır.
# Belirli bir alıcıya giden maillerin durumunu kontrol et
grep "[email protected]" /var/log/mail.log | tail -50
# Bounce olan mailleri filtrele
grep "status=bounced" /var/log/mail.log | tail -100
# Bugünkü tüm hataları say
grep "$(date +%b %e)" /var/log/mail.log | grep -c "status=bounced"
Postfix log satırı şöyle görünür:
Jan 15 14:23:45 mailserver postfix/smtp[12345]: ABC123: to=<[email protected]>,
relay=mail.karsialan.com[203.0.113.10]:25, delay=2.3, delays=0.1/0/1.8/0.4,
dsn=5.1.1, status=bounced (host mail.karsialan.com[203.0.113.10] said:
550 5.1.1 <[email protected]>: Recipient address rejected: User unknown
in virtual mailbox table (in reply to RCPT TO command))
Bu satırı parçalara ayıralım:
- ABC123: Postfix kuyruk ID’si
- relay: Bağlandığımız sunucu ve IP
- dsn=5.1.1: Enhanced status code
- status=bounced: Mail iletilmedi
- Parantez içindeki kısım: Karşı sunucunun verdiği yanıt
# Kuyruk ID'sine göre tüm log satırlarını bul
grep "ABC123" /var/log/mail.log
# En çok bounce alan domain'leri listele
grep "status=bounced" /var/log/mail.log |
grep -oP "to=<[^>]+>" |
grep -oP "@[^>]+" |
sort | uniq -c | sort -rn | head -20
Gerçek Dünya Senaryosu 1: Kampanya Mailleri Gitmiyordu
Bir e-ticaret müşterisinin mail sistemini yönetiyorduk. Pazarlama ekibi 50.000 kişilik bir listeye kampanya maili gönderdi ve sabah baktıklarında bounce oranının yüzde 35’e çıktığını gördüler. Panik havasında bana geldiler.
İlk adım logları incelemekti:
# Son 24 saatin bounce istatistikleri
grep "$(date +%b %e)" /var/log/mail.log |
grep "status=bounced" |
grep -oP "dsn=d.d.d" |
sort | uniq -c | sort -rn
Çıktı şöyle geldi:
2847 dsn=5.7.1
8234 dsn=5.1.1
6891 dsn=5.7.26
dsn=5.7.26 dikkatimi çekti. Bu kod SPF veya DMARC başarısızlığını gösterir. Mail sunucusunun SPF kaydına baktım:
# SPF kaydını kontrol et
dig TXT ornekdomain.com | grep "v=spf1"
# Daha detaylı sorgu
host -t TXT ornekdomain.com
Sorun ortaya çıktı. Kampanya mailleri üçüncü parti bir mail servisinden gönderiliyordu ama o servisin IP aralıkları SPF kaydına eklenmemişti. Hızlı çözüm:
# Mevcut SPF kaydını güncelle
# DNS panelinden şu kaydı ekle:
# v=spf1 include:ornekdomain.com include:mailchimp-provider.com ~all
# Değişikliği doğrula
dig TXT ornekdomain.com +short
Gerçek Dünya Senaryosu 2: Kara Liste Sorunu
Başka bir senaryoda sunucumuzdan gönderilen maillerin büyük telekom operatörlerinin mail sistemlerine gittiğini ama küçük hosting firmaları ve bazı kurumsal mail sunucularına gidemediğini fark ettik.
# Mail kuyruğundaki bekleyen mailleri kontrol et
mailq | head -50
# Postfix kuyruğunu daha detaylı gör
postqueue -p | grep -E "^^|^[A-F0-9]"
# Kuyruktaki belirli domain'lere ait mailleri say
postqueue -p | grep "@hedefdomain.com" | wc -l
Bounce mesajlarının içeriğine baktım:
550 5.7.1 Message rejected due to: IP reputation check failed.
Visit https://blacklistcheck.ornekrbl.com for more information.
IP’nin kara listede olup olmadığını kontrol etmek için:
# Manuel kara liste kontrolü
MAILIP="203.0.113.50"
# MXToolbox alternatifi olarak komut satırından
for rbl in zen.spamhaus.org bl.spamcop.net dnsbl.sorbs.net b.barracudacentral.org; do
result=$(dig +short "${MAILIP##*.}.${MAILIP%.*}.${MAILIP%.*.*}.${MAILIP%%.*}.${rbl}")
if [ -n "$result" ]; then
echo "LISTED in $rbl: $result"
else
echo "Clean in $rbl"
fi
done
IP’nin Spamhaus’ta listelendiğini gördük. Sorunun kaynağını bulmak için o IP’den o gün gönderilen mailleri inceledim:
# Belirli IP'den gönderilen mailleri logda bul
grep "client=[203.0.113.50]" /var/log/mail.log |
grep "$(date +%b %e)" | head -50
# Saatlik gönderim yoğunluğunu analiz et
grep "$(date +%b %e)" /var/log/mail.log |
grep "status=sent" |
awk '{print $1, $2, $3}' |
cut -d: -f1 | sort | uniq -c
Gece 3’te anormal bir spike vardı. Sunucuda bir hesap ele geçirilmişti ve spam gönderilmişti. Hesabı kapattık, IP’yi kara liste kaldırma işlemine soktuk ve rate limiting ekledik.
Bounce Mail Header’larını Elle Analiz Etme
Bazen otomatik araçlar yetersiz kalır ve bounce’u başından sonuna elle okumak gerekir. Bir bounce mailin tam header’ı şöyle görünür:
# Procmail veya mutt ile bir bounce'u kaydet ve incele
# Veya doğrudan mailbox dosyasından çıkar
formail -s cat < /var/mail/admin | grep -B5 -A200 "mailer-daemon"
Tipik bir bounce header analizi:
Return-Path: <>
Received: from mail.ornekdomain.com (mail.ornekdomain.com [203.0.113.1])
by mx.hedefdomain.com with ESMTP id abc123
for <[email protected]>; Mon, 15 Jan 2024 14:23:45 +0300
Content-Type: multipart/report; report-type=delivery-status;
boundary="===============1234567890=="
--===============1234567890==
Content-Type: message/delivery-status
Reporting-MTA: dns; mail.ornekdomain.com
Action: failed
Status: 5.1.1
Diagnostic-Code: smtp; 550 5.1.1 User unknown
Content-Type: multipart/report olan maillerde üç MIME bölümü bulunur:
- Birinci bölüm: İnsan okunabilir açıklama
- İkinci bölüm: message/delivery-status, makine okunabilir DSN bilgisi
- Üçüncü bölüm: message/rfc822, orijinal mailin kopyası
Postfix ile Bounce Yönetimi
Postfix bounce davranışını özelleştirmek için birkaç önemli konfigürasyon parametresi var:
# main.cf içindeki bounce ilgili ayarları gör
postconf | grep -E "bounce|notify|maximal_queue"
# Önemli parametreler
postconf bounce_notice_recipient
postconf notify_classes
postconf bounce_queue_lifetime
postconf maximal_queue_lifetime
Bounce bildirimleri yönetimi için main.cf ayarları:
# /etc/postfix/main.cf düzenlemesi
# Bounce bildirimlerini admin'e de gönder
notify_classes = bounce, delay, policy, protocol, resource, software
bounce_notice_recipient = [email protected]
# 2FA bounce süresi (varsayılan 5 gün)
bounce_queue_lifetime = 2d
maximal_queue_lifetime = 5d
# Değişiklikleri uygula
postfix reload
Bounce Analizi için Script Yazmak
Manuel analiz zaman alıcıdır. Günlük bounce raporları için basit bir script işinizi çok kolaylaştırır:
#!/bin/bash
# bounce_report.sh - Günlük bounce raporu
LOG="/var/log/mail.log"
TARIH=$(date +%b %e)
RAPOR="/tmp/bounce_raporu_$(date +%Y%m%d).txt"
echo "=== Bounce Raporu: $(date) ===" > $RAPOR
echo "" >> $RAPOR
# Toplam bounce sayısı
TOPLAM=$(grep "$TARIH" $LOG | grep -c "status=bounced")
echo "Toplam bounce: $TOPLAM" >> $RAPOR
# Hata koduna göre dağılım
echo "" >> $RAPOR
echo "--- Hata Kodları ---" >> $RAPOR
grep "$TARIH" $LOG | grep "status=bounced" |
grep -oP "dsn=K[d.]+" |
sort | uniq -c | sort -rn >> $RAPOR
# En çok bounce alan domain'ler
echo "" >> $RAPOR
echo "--- En Çok Bounce Alan Domainler (Top 10) ---" >> $RAPOR
grep "$TARIH" $LOG | grep "status=bounced" |
grep -oP "to=<[^>]+>" |
grep -oP "@K[^>]+" |
sort | uniq -c | sort -rn | head -10 >> $RAPOR
# Kara liste ipucları
echo "" >> $RAPOR
echo "--- Muhtemel Kara Liste Redleri ---" >> $RAPOR
grep "$TARIH" $LOG | grep "status=bounced" |
grep -iE "blacklist|blocked|spam|rbl|reputation" |
wc -l >> $RAPOR
cat $RAPOR
# Opsiyonel: Mail ile gönder
# mail -s "Günlük Bounce Raporu" [email protected] < $RAPOR
DMARC Raporlarını Okuma
DMARC raporları da aslında bir tür bounce analizi aracıdır. XML formatında gelirler ve SPF/DKIM başarısızlıklarını raporlarlar:
# DMARC raporunu aç ve parse et
# Önce zip'i aç
unzip dmarc_rapor.zip -d /tmp/dmarc/
# XML'i insan okunabilir hale getir
cat /tmp/dmarc/*.xml | python3 -c "
import sys
import xml.dom.minidom
content = sys.stdin.read()
print(xml.dom.minidom.parseString(content).toprettyxml(indent=' '))
" | grep -A5 -B5 "fail"
DMARC raporundaki kritik alanlar:
- source_ip: Maili gönderen IP
- count: Bu IP’den gönderilen mail sayısı
- disposition: Alıcı sunucunun ne yaptığı (none, quarantine, reject)
- dkim result: DKIM doğrulama sonucu
- spf result: SPF doğrulama sonucu
Bounce Loop Tuzağından Kaçınmak
Bounce loop, bir bounce mailin kendisinin de bounce yemesi durumudur. Bu durum sunucuyu felç edebilir. Postfix bunu Return-Path: ile önler ama yanlış konfigürasyonlarda sorun çıkabilir:
# Bounce loop olup olmadığını kontrol et
grep "mailer-daemon" /var/log/mail.log |
grep "$(date +%b %e)" | wc -l
# Kuyruktaki mailer-daemon kaynaklı mailleri gör
postqueue -p | grep "MAILER-DAEMON"
# Eğer loop varsa kuyruğu temizle (dikkatli!)
postsuper -d ALL deferred
Exchange ve Office365 Bounce Kodları
Eğer hedef sunucu Exchange veya Office365 ise, bounce mesajları biraz farklı görünür:
550 5.4.1 Recipient address rejected: Access denied.
AS(201806281)
Bu gibi durumlarda Microsoft’un hata kod veritabanına başvurmak gerekir. Sık görülen Office365 kodları:
- 5.4.1: SPF veya sender policy başarısızlığı
- 5.7.64: TenantAttribution. Tenant’ın kimlik doğrulama sorunu
- 5.7.124: Gönderici bu dağıtım listesine izinli değil
- 5.1.10: Alıcı adresi geçersiz veya yok
# Office365'e giden maillerin durumunu izle
grep "@onmicrosoft.com|@outlook.com|@hotmail.com" /var/log/mail.log |
grep "status=bounced" |
grep -oP "said: K.*" |
sort | uniq -c | sort -rn | head -20
Bounce Oranını Düşürmek için Pratik Adımlar
Bounce analizi yapıp sorunu tespit ettikten sonra ne yapmalısınız? İşte pratik adımlar:
- Liste hijyeni: Hard bounce alan adresleri derhal listeden çıkarın. Bounce oranı yüzde 2’yi geçmeye başlarsa alarm verin.
- SPF kaydı: Tüm mail gönderen IP ve servislerinizin SPF kaydında olduğundan emin olun.
- DKIM imzalama: Her domain için DKIM key oluşturun ve tüm mailleri imzalayın.
- DMARC politikası: Önce
p=noneile başlayın, raporları analiz edin, sonraquarantinevereject‘e geçin. - Rate limiting: Dakikada belirli sayının üzerinde mail gönderimini engelleyin.
- Warm-up: Yeni IP’lerden gönderim yaparken yavaş başlayıp kademeli artırın.
# Postfix'te rate limiting
# main.cf
# smtpd_client_connection_rate_limit = 100
# smtpd_client_message_rate_limit = 200
# Mevcut rate limitlerini kontrol et
postconf | grep -i rate_limit
# Anlık SMTP bağlantı sayısını izle
ss -tn | grep :25 | wc -l
Bounce Takip Sistemi Kurmak
Manuel takip uzun vadede sürdürülebilir değil. Basit bir veritabanı tabanlı takip sistemi kurmak için:
#!/bin/bash
# bounce_tracker.sh - Bounce'ları SQLite'a kaydet
DB="/var/lib/mail-stats/bounces.db"
# Veritabanını oluştur
sqlite3 $DB "CREATE TABLE IF NOT EXISTS bounces (
tarih TEXT,
alici TEXT,
domain TEXT,
hata_kodu TEXT,
hata_mesaji TEXT,
kayit_zamani DATETIME DEFAULT CURRENT_TIMESTAMP
);"
# Bugünkü bounce'ları parse edip ekle
grep "$(date +%b %e)" /var/log/mail.log |
grep "status=bounced" | while read line; do
ALICI=$(echo "$line" | grep -oP "to=<K[^>]+")
DOMAIN=$(echo "$ALICI" | cut -d@ -f2)
KOD=$(echo "$line" | grep -oP "dsn=K[d.]+")
MESAJ=$(echo "$line" | grep -oP "said: K[^)]+")
TARIH=$(echo "$line" | awk '{print $1, $2, $3}')
sqlite3 $DB "INSERT INTO bounces (tarih, alici, domain, hata_kodu, hata_mesaji)
VALUES ('$TARIH', '$ALICI', '$DOMAIN', '$KOD', '${MESAJ//'/''}');"
done
echo "Kayıt tamamlandı."
sqlite3 $DB "SELECT domain, COUNT(*) as sayi FROM bounces
WHERE kayit_zamani > datetime('now', '-1 day')
GROUP BY domain ORDER BY sayi DESC LIMIT 10;"
Sonuç
Bounce mail analizi bir sanatır; ham veriyi okuyup sorunun kaynağına inebilmek için hem SMTP protokolünü hem de sisteminizin davranışını iyi tanımanız gerekir. En önemli nokta şudur: bir bounce mesajı sizi şikayet etmez, size bilgi verir. O bilgiyi doğru okuyabilirseniz çözüm genellikle çok uzakta değildir.
Pratik olarak yapmanız gerekenler şunlardır: Her gün bounce loglarını gözlemleyin, yüzde 2’nin üzerinde hard bounce oranı görürseniz hemen müdahale edin, SPF/DKIM/DMARC üçlüsünü mutlaka kurun ve çalışır hale getirin, kara liste durumunu haftalık kontrol edin ve liste temizliğini asla ihmal etmeyin.
Mail teslimat sorunları genellikle tek bir noktada değil, birden fazla katmanda birleşen küçük sorunlardan kaynaklanır. Bounce analizi o katmanları tek tek soymanızı sağlar. Sabırlı olun, logları okuyun ve her bounce mesajına “bu bana ne söylüyor?” diye sorun.
