Gönderilen Maillerde Karakter Kodlama Sorunu: UTF-8 ve Encoding Hatalarını Çözme

Mail sunucunuzdan gönderilen e-postalar alıcı tarafında bozuk karakterlerle mi görünüyor? “ş”, “ğ”, “ü”, “ö”, “ç”, “ı” gibi Türkçe karakterler yerine anlamsız semboller mi çıkıyor? Bu sorun, sysadmin olarak kariyerinizin bir noktasında mutlaka karşılaşacağınız klasik bir başağrısıdır. Özellikle Postfix, Sendmail veya uygulama katmanından gönderilen maillerde encoding sorunları can sıkıcı bir hal alabilir. Bu yazıda sorunu köklü biçimde çözmeniz için gereken her şeyi ele alacağız.

Sorunun Kaynağını Anlamak

Karakter kodlama sorunları birkaç farklı katmanda ortaya çıkabilir. Bunu anlamadan çözüme atlamak, yangını söndürmeden önce nerede yandığını bilmemek gibidir.

Mail gönderim zincirinde encoding şu noktalarda bozulabilir:

  • Uygulamanın mail oluşturma katmanı (PHP, Python, Java uygulamaları)
  • MTA (Mail Transfer Agent) yapılandırması (Postfix, Sendmail, Exim)
  • MIME başlıkları ve Content-Type tanımlamaları
  • Mail istemcisinin yorumlama biçimi

Temel olarak bir e-posta UTF-8 ile yazılmış olsa bile, bunu alıcıya doğru şekilde iletmek için MIME başlıklarının da bunu açıkça belirtmesi gerekir. Aksi hâlde alıcının mail istemcisi hangi karakter setini kullanacağını bilemez ve varsayılan olarak genellikle ISO-8859-1 veya ASCII’ye döner.

Encoding Sorununu Teşhis Etmek

Sorunu çözmeden önce neyin bozuk olduğunu net olarak görmemiz gerekiyor. İlk adım ham mail başlıklarını incelemek.

Ham Mail Başlıklarını Kontrol Etme

Postfix log dosyasından sorunlu bir mail ID’si bulun ve o mailin ham içeriğine bakın:

# Mail kuyruğundaki mesajları listele
postqueue -p

# Belirli bir mesajın içeriğini görüntüle
postcat -q MESAJ_ID

# Postfix mail loglarını gerçek zamanlı izle
tail -f /var/log/mail.log | grep -i "encoding|charset|utf"

Bir mailin ham başlıklarında şunu görüyorsanız sorun var demektir:

Content-Type: text/plain

Oysa olması gereken şu şekildedir:

Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Python ile Mail İçeriğini Analiz Etme

Aşağıdaki script ile gelen ya da giden bir .eml dosyasının encoding bilgilerini hızlıca analiz edebilirsiniz:

#!/usr/bin/env python3
import email
import sys

def analyze_mail_encoding(eml_file):
    with open(eml_file, 'rb') as f:
        msg = email.message_from_bytes(f.read())
    
    print(f"Subject: {msg['subject']}")
    print(f"Content-Type: {msg.get_content_type()}")
    print(f"Charset: {msg.get_content_charset()}")
    print(f"Content-Transfer-Encoding: {msg['content-transfer-encoding']}")
    
    for part in msg.walk():
        if part.get_content_type() == 'text/plain':
            charset = part.get_content_charset() or 'bilinmiyor'
            print(f"nPart charset: {charset}")
            try:
                payload = part.get_payload(decode=True)
                print(f"Decode edilebilir: Evet")
                print(f"İlk 100 karakter: {payload[:100].decode(charset)}")
            except Exception as e:
                print(f"Decode hatası: {e}")

if __name__ == "__main__":
    analyze_mail_encoding(sys.argv[1])

Bu scripti çalıştırmak için:

python3 analyze_mail.py sorunlu_mail.eml

Postfix Yapılandırmasında UTF-8 Desteği

Postfix’te encoding sorunlarının büyük çoğunluğu yanlış ya da eksik yapılandırmadan kaynaklanır.

main.cf Düzenlemeleri

# /etc/postfix/main.cf dosyasını düzenle
nano /etc/postfix/main.cf

Aşağıdaki parametrelerin doğru ayarlandığından emin olun:

  • smtpd_banner: Sunucu kimlik bilgisi, ASCII karakterlerle sınırlı tutun
  • mime_header_checks: MIME başlık denetim dosyasını burada tanımlarsınız
  • header_checks: Genel başlık kontrolü için kullanılır
  • append_dot_mydomain: Adres çözümlemesini etkiler, no olarak bırakın
# main.cf içine eklenecek UTF-8 ilgili parametreler
cat >> /etc/postfix/main.cf << 'EOF'

# UTF-8 ve MIME ayarları
mime_header_checks = regexp:/etc/postfix/mime_header_checks
header_checks = regexp:/etc/postfix/header_checks
disable_mime_output_conversion = no
EOF

MIME Header Checks Dosyası

# /etc/postfix/mime_header_checks dosyasını oluştur
cat > /etc/postfix/mime_header_checks << 'EOF'
# Content-Type başlığı charset içermiyorsa UTF-8 ekle
/^Content-Type: text/(plain|html)s*$/  REPLACE Content-Type: text/$1; charset=UTF-8
EOF

# Değişiklikleri uygula
postfix reload

PHP ile Mail Gönderiminde Encoding Düzeltmesi

Web uygulamalarından gönderilen maillerde encoding sorunları çok yaygındır. PHP’nin native mail() fonksiyonu encoding konusunda oldukça kısıtlıdır.

PHP mail() Fonksiyonunda Doğru Encoding

<?php
function send_utf8_mail($to, $subject, $body, $from) {
    // Subject'i RFC 2047'ye göre encode et
    $encoded_subject = '=?UTF-8?B?' . base64_encode($subject) . '?=';
    
    // From başlığında da Türkçe karakter varsa encode et
    $encoded_from = '=?UTF-8?B?' . base64_encode('Gönderen Ad') . '?= <' . $from . '>';
    
    $headers = implode("rn", [
        'MIME-Version: 1.0',
        'Content-Type: text/html; charset=UTF-8',
        'Content-Transfer-Encoding: base64',
        'From: ' . $encoded_from,
        'Reply-To: ' . $from,
        'X-Mailer: PHP/' . phpversion()
    ]);
    
    // Body'yi base64 ile encode et
    $encoded_body = base64_encode($body);
    
    return mail($to, $encoded_subject, $encoded_body, $headers);
}

// Kullanım örneği
$subject = "Sipariş Onayı - Türkçe Karakterler Düzgün Görünecek";
$body = "<html><body><p>Merhaba, şifreniz güncellendi.</p></body></html>";
send_utf8_mail("[email protected]", $subject, $body, "[email protected]");
?>

PHPMailer ile Güvenilir Encoding

Gerçek dünya senaryolarında mail() yerine PHPMailer kullanmanızı şiddetle tavsiye ederim. Encoding sorunlarını otomatik olarak halleder:

<?php
use PHPMailerPHPMailerPHPMailer;
use PHPMailerPHPMailerSMTP;

require 'vendor/autoload.php';

$mail = new PHPMailer(true);

try {
    $mail->isSMTP();
    $mail->Host       = 'mail.sirketiniz.com';
    $mail->SMTPAuth   = true;
    $mail->Username   = '[email protected]';
    $mail->Password   = 'parola';
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
    $mail->Port       = 587;
    
    // Kritik satır: Charset'i açıkça UTF-8 yap
    $mail->CharSet    = PHPMailer::CHARSET_UTF8;
    $mail->Encoding   = PHPMailer::ENCODING_BASE64;
    
    $mail->setFrom('[email protected]', 'Türkçe İsim Sorunsuz');
    $mail->addAddress('[email protected]', 'Alıcı Adı');
    
    $mail->isHTML(true);
    $mail->Subject = 'Özel Karakterler: ş ğ ü ö ç ı';
    $mail->Body    = '<h1>Merhaba!</h1><p>Bu mail UTF-8 ile gönderildi.</p>';
    $mail->AltBody = 'Merhaba! Bu mail UTF-8 ile gönderildi.';
    
    $mail->send();
    echo 'Mail başarıyla gönderildi.';
    
} catch (Exception $e) {
    echo "Mail gönderilemedi: {$mail->ErrorInfo}";
}
?>

Python ile Mail Gönderiminde Encoding

Python’da da encoding sorunları sıkça yaşanır. Özellikle eski smtplib kodlarında str ve bytes karışıklığı büyük sorun yaratır.

#!/usr/bin/env python3
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr

def send_utf8_email(smtp_host, smtp_port, username, password, 
                     to_addr, subject, body_html, body_text):
    
    msg = MIMEMultipart('alternative')
    
    # Subject'i doğru encode et
    msg['Subject'] = Header(subject, 'utf-8')
    
    # From alanında Türkçe karakter varsa formataddr kullan
    msg['From'] = formataddr((str(Header('Şirket Destek', 'utf-8')), username))
    msg['To'] = to_addr
    msg['MIME-Version'] = '1.0'
    
    # Text ve HTML partları oluştur
    # charset='utf-8' parametresi Content-Type başlığını otomatik ayarlar
    part_text = MIMEText(body_text, 'plain', 'utf-8')
    part_html = MIMEText(body_html, 'html', 'utf-8')
    
    # Önce text, sonra HTML ekle (tercih sırası)
    msg.attach(part_text)
    msg.attach(part_html)
    
    # SMTP ile gönder
    with smtplib.SMTP(smtp_host, smtp_port) as server:
        server.ehlo()
        server.starttls()
        server.login(username, password)
        server.sendmail(username, to_addr, msg.as_bytes())
        print("Mail başarıyla gönderildi.")

# Kullanım
send_utf8_email(
    smtp_host='mail.sirket.com',
    smtp_port=587,
    username='[email protected]',
    password='gizli_parola',
    to_addr='[email protected]',
    subject='Türkçe karakterli konu: Şifre Sıfırlama',
    body_html='<p>Merhaba, şifrenizi sıfırlamak için <a href="#">tıklayın</a>.</p>',
    body_text='Merhaba, şifrenizi sıfırlamak için linke tıklayın.'
)

Sistem Locale Ayarlarının Önemi

Pek çok sysadmin bu kısmı gözden kaçırır. Postfix ve diğer mail servisleri, sistemin locale ayarlarından etkilenebilir.

# Mevcut locale ayarlarını kontrol et
locale

# Sistemde mevcut UTF-8 locale'leri listele
locale -a | grep -i utf

# Eğer tr_TR.UTF-8 yoksa oluştur
locale-gen tr_TR.UTF-8

# Sistem genelinde locale ayarla
update-locale LANG=tr_TR.UTF-8 LC_ALL=tr_TR.UTF-8

# Postfix servisinin environment'ını kontrol et
systemctl show postfix | grep Environment

# Postfix için locale'i service dosyasında belirt
systemctl edit postfix

systemctl edit postfix komutu ile açılan dosyaya şunu ekleyin:

[Service]
Environment="LANG=tr_TR.UTF-8"
Environment="LC_ALL=tr_TR.UTF-8"

Ardından servisi yeniden başlatın:

systemctl daemon-reload
systemctl restart postfix

Gerçek Dünya Senaryosu: E-Ticaret Sitesinden Bozuk Mail

Bir e-ticaret müşterisinde şöyle bir sorunla karşılaştım: Müşteriye gönderilen sipariş onay mailleri Outlook’ta düzgün görünürken Gmail’de Türkçe karakterler bozuluyordu. Bu asimetrik durum ilginçti.

Teşhis süreci:

# Gönderilen maillerden birini yakalayıp analiz et
# Postfix'te bcc ile bir kopyayı debug adresine yönlendir
echo "debug_bcc_address = [email protected]" >> /etc/postfix/main.cf
postfix reload

# Gelen debug mailini .eml olarak kaydet ve incele
cat sorunlu_mail.eml | grep -i "content-type|charset|encoding"

Çıktı:

Content-Type: text/html
Content-Transfer-Encoding: 8bit

Problem netti. Content-Transfer-Encoding: 8bit ve charset eksikliği vardı. Gmail bu durumda farklı davranıyordu çünkü Outlook gönderen sunucunun IP’sine bakarak Türkçe charset tahmin ediyordu (yanlış bir iyilik), Gmail ise standarda sıkı sıkıya bağlı kalıyordu.

Çözüm: Uygulama geliştiricisiyle koordineli olarak PHPMailer’a geçtik ve yukarıda gösterdiğim charset ayarlarını uyguladık. Bunun yanı sıra Postfix tarafında mime_header_checks ile bir güvenlik ağı kurduk.

Mimetools ile Test ve Doğrulama

Encoding düzeltmelerini uyguladıktan sonra test etmeniz kritik önem taşır.

# swaks aracıyla test maili gönder ve başlıkları kontrol et
# swaks kurulu değilse: apt install swaks
swaks --to [email protected] 
      --from [email protected] 
      --server mail.sirket.com 
      --port 587 
      --auth LOGIN 
      --auth-user [email protected] 
      --auth-password "parola" 
      --header "Subject: Türkçe Test: ş ğ ü ö ç ı İ Ğ Ü Ö Ş Ç" 
      --header "Content-Type: text/plain; charset=UTF-8" 
      --header "Content-Transfer-Encoding: quoted-printable" 
      --body "Bu bir UTF-8 test mailidir. Özel karakterler: şğüöçı"

# Gönderilen mailin Postfix logunda nasıl göründüğüne bak
grep "[email protected]" /var/log/mail.log | tail -20

Encoding Doğrulama Script’i

Gönderim sonrasında encoding’in doğru olup olmadığını kontrol eden basit bir bash script’i:

#!/bin/bash
# check_mail_encoding.sh
# Kullanım: ./check_mail_encoding.sh /path/to/mail.eml

EML_FILE="$1"

if [ -z "$EML_FILE" ]; then
    echo "Kullanim: $0 <eml_dosyasi>"
    exit 1
fi

echo "=== Mail Encoding Analizi ==="
echo ""

# Content-Type kontrolü
CT=$(grep -i "^Content-Type:" "$EML_FILE" | head -1)
echo "Content-Type: $CT"

if echo "$CT" | grep -qi "charset"; then
    echo "Charset bilgisi: VAR"
    CHARSET=$(echo "$CT" | grep -oi "charset=[^;[:space:]]*" | cut -d= -f2)
    echo "Kullanilan charset: $CHARSET"
else
    echo "UYARI: Charset bilgisi EKSIK!"
fi

echo ""

# Content-Transfer-Encoding kontrolü
CTE=$(grep -i "^Content-Transfer-Encoding:" "$EML_FILE" | head -1)
echo "Content-Transfer-Encoding: $CTE"

if echo "$CTE" | grep -qi "base64|quoted-printable"; then
    echo "Transfer encoding: UYGUN"
else
    echo "UYARI: Transfer encoding 8bit veya eksik, sorun yaratabilir!"
fi

echo ""

# Subject encoding kontrolü
SUBJ=$(grep -i "^Subject:" "$EML_FILE" | head -1)
echo "Subject: $SUBJ"

if echo "$SUBJ" | grep -q "=?"; then
    echo "Subject encoding: RFC 2047 uyumlu"
else
    echo "UYARI: Subject encode edilmemis, Turkce karakter varsa sorun olabilir!"
fi

echo ""
echo "=== Analiz Tamamlandi ==="
chmod +x check_mail_encoding.sh
./check_mail_encoding.sh /tmp/test_mail.eml

Sık Karşılaşılan Hatalar ve Çözümleri

“=?iso-8859-9?Q?…” şeklinde görünen subject başlıkları

Bu durum uygulamanın ISO-8859-9 (Türkçe karakter seti) kullandığı anlamına gelir. Bazı eski PHP uygulamaları bunu yapar. Postfix seviyesinde header_checks ile yakalayabilir ve UTF-8’e yönlendirebilirsiniz, ancak en temiz çözüm uygulamayı düzeltmektir.

“Content-Transfer-Encoding: 8bit” ile gönderilen Türkçe mailler

8bit encoding bazı sunucularla uyumsuz olabilir. quoted-printable veya base64 kullanmak çok daha güvenlidir. Özellikle uzak sunuculara relay yapıyorsanız 8bit’i kesinlikle terk edin.

Postfix’in charset’i değiştirmesi

Postfix bazı durumlarda MIME conversion yapabilir. Bunu engellemek için:

# main.cf içine ekle
disable_mime_output_conversion = yes

Bu parametreyi ekleyip servisini yeniden başlatın:

postfix reload

From veya Reply-To alanında bozuk karakterler

RFC 5322’ye göre From alanındaki görüntü adı ASCII dışı karakter içeriyorsa mutlaka RFC 2047 formatında encode edilmelidir. =?UTF-8?B?BASE64_ENCODED?= formatını kullanın.

Monitoring ve Önleyici Tedbirler

Sorun tekrarlamaması için izleme mekanizmaları kurmanızı öneririm.

# Encoding hatalarını logdan izleyen basit bir cron script'i
cat > /usr/local/bin/check_mail_encoding_errors.sh << 'EOF'
#!/bin/bash
LOGFILE="/var/log/mail.log"
ALERT_MAIL="[email protected]"
DATE=$(date -d "1 hour ago" "+%b %e %H")

ERRORS=$(grep "$DATE" "$LOGFILE" | grep -i "encoding|charset|mime" | wc -l)

if [ "$ERRORS" -gt 10 ]; then
    echo "Son 1 saatte $ERRORS adet encoding ilgili log satiri tespit edildi." | 
    mail -s "Mail Encoding Uyarisi" "$ALERT_MAIL"
fi
EOF

chmod +x /usr/local/bin/check_mail_encoding_errors.sh

# Crontab'a ekle
echo "0 * * * * /usr/local/bin/check_mail_encoding_errors.sh" | crontab -

Sonuç

Mail encoding sorunları, yüzeysel baktığınızda basit görünebilir ama aslında birkaç katmanın birbiriyle uyum içinde çalışmasını gerektiren nüanslı bir konudur. Özetlemek gerekirse:

  • Sorunu her zaman ham başlıklardan başlayarak teşhis edin
  • PHP projelerinde mail() yerine PHPMailer gibi kütüphaneleri tercih edin
  • Python’da email.mime modülünü doğru kullanın ve charset parametresini asla atlamayın
  • Postfix’te mime_header_checks ile bir güvenlik ağı oluşturun
  • Sistem locale’inin UTF-8 olduğundan emin olun
  • swaks ile düzenli test gönderimleri yapın
  • Content-Transfer-Encoding olarak base64 veya quoted-printable kullanın, 8bitten kaçının

Bu adımları izlediğinizde Türkçe karakterlerin bozulması meselesini kalıcı olarak çözebilirsiniz. Eğer tüm bunları yapmanıza rağmen sorun devam ediyorsa, sorunun alıcı tarafındaki mail istemcisinden kaynaklanıyor olabileceğini de göz önünde bulundurun. Bazı kurumsal Outlook yapılandırmaları incoming mail encoding’i farklı yorumlayabilir. Bu durumda sorun sizde değil, karşı taraftadır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir