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.mimemodülünü doğru kullanın ve charset parametresini asla atlamayın - Postfix’te
mime_header_checksile bir güvenlik ağı oluşturun - Sistem locale’inin UTF-8 olduğundan emin olun
swaksile düzenli test gönderimleri yapınContent-Transfer-Encodingolarakbase64veyaquoted-printablekullanı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.
