Apache ile Let’s Encrypt Sertifika Sorunları ve Çözümleri
Let’s Encrypt ile Apache’yi birlikte kullanmak kulağa kolay gelir, ve çoğu zaman öyledir. Ama bir şeyler ters gittiğinde, özellikle sertifika yenileme sessizce başarısız olduğunda ve bunu üç ay sonra sitenin HTTPS’i düşünce fark ettiğinizde, o an gerçekten can sıkıcıdır. Bu yazıda Apache üzerinde karşılaşılan yaygın Let’s Encrypt sorunlarını, hata mesajlarının ne anlama geldiğini ve bunları nasıl çözeceğinizi adım adım ele alacağız.
Let’s Encrypt ve Certbot Nasıl Çalışır?
Sorunları çözmeden önce temel mekanizmayı anlamak işinizi çok kolaylaştırır. Let’s Encrypt, domain sahipliğini doğrulamak için birkaç farklı challenge (meydan okuma) yöntemi kullanır.
Apache ile en sık kullanılan yöntem HTTP-01 challenge‘dır. Bu yöntemde Certbot, web sunucunuzun /.well-known/acme-challenge/ dizinine geçici bir dosya koyar ve Let’s Encrypt sunucuları bu dosyaya HTTP üzerinden erişmeye çalışır. Erişim başarılıysa domain sahibi olduğunuz doğrulanır ve sertifika verilir.
DNS-01 challenge ise DNS kayıtlarınıza bir TXT kaydı ekleyerek çalışır. Wildcard sertifikalar için zorunludur ve web sunucusuna erişim gerektirmez.
Bu temel bilgiyi aklınızda tutun, çünkü hataların büyük çoğunluğu bu doğrulama adımında gerçekleşir.
Sık Karşılaşılan Hata Kategorileri
1. HTTP-01 Challenge Başarısız Oluyor
Bu en yaygın hata kategorisidir. Hata mesajı genellikle şu şekilde görünür:
Challenge failed for domain example.com
http-01 challenge for example.com
Waiting for verification...
Challenge failed for domain example.com
IMPORTANT NOTES:
- The following errors were found:
Domain: example.com
Type: unauthorized
Detail: Invalid response from http://example.com/.well-known/acme-challenge/...
Bu hatanın birkaç farklı nedeni olabilir. Önce .well-known/acme-challenge/ dizinine manuel erişim testi yaparak başlayın:
# Test dosyası oluştur
mkdir -p /var/www/html/.well-known/acme-challenge/
echo "test" > /var/www/html/.well-known/acme-challenge/testfile
# Dışarıdan erişimi test et (başka bir makineden veya curl ile)
curl -I http://example.com/.well-known/acme-challenge/testfile
# Test dosyasını temizle
rm /var/www/html/.well-known/acme-challenge/testfile
Eğer 403 Forbidden alıyorsanız, Apache konfigürasyonunuzda bu dizin için erişim engeli var demektir. Apache’nin bazı konfigürasyonlarında Options -Indexes veya Require all denied gibi direktifler tüm .well-known yolunu etkiliyor olabilir.
# /etc/apache2/conf-available/well-known.conf dosyası oluşturun
cat > /etc/apache2/conf-available/well-known.conf << 'EOF'
Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
<Directory "/var/www/letsencrypt/.well-known/acme-challenge/">
Options None
AllowOverride None
Require all granted
ForceType text/plain
</Directory>
EOF
mkdir -p /var/www/letsencrypt/.well-known/acme-challenge/
a2enconf well-known
systemctl reload apache2
Bu yaklaşım, tüm virtual host’larınız için merkezi bir ACME challenge dizini tanımlar. Certbot’u da bu dizini kullanacak şekilde çalıştırmanız gerekir:
certbot certonly --webroot -w /var/www/letsencrypt -d example.com -d www.example.com
2. HTTPS’e Yönlendirme Sorunu
Birçok Apache konfigürasyonu HTTP trafiğini doğrudan HTTPS’e yönlendirir. Bu yönlendirme, Let’s Encrypt’in HTTP-01 doğrulamasını geçersiz kılar çünkü challenge URL’si önce HTTPS’e yönlenir, oradan da sertifika hatası alınır.
Tipik sorunlu konfigürasyon:
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
Bu konfigürasyonda /.well-known/acme-challenge/ yolu da HTTPS’e yönlenir. Çözüm, yönlendirmeyi challenge yolundan muaf tutmaktır:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
# ACME challenge yolunu yönlendirmeden muaf tut
Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
<Directory "/var/www/letsencrypt/.well-known/acme-challenge/">
Options None
AllowOverride None
Require all granted
</Directory>
# Geriye kalan her şeyi HTTPS'e yönlendir
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/
RewriteRule ^(.*)$ https://example.com$1 [R=301,L]
</VirtualHost>
3. Firewall ve Port Sorunu
Let’s Encrypt sunucuları sizin sunucunuza dışarıdan bağlanır. 80 numaralı port kapalıysa ya da firewall engelliyorsa doğrulama hiçbir zaman tamamlanamaz.
# Port 80'in dışarıdan erişilebilir olup olmadığını kontrol et
nmap -p 80 example.com
# UFW kullanıyorsanız
ufw status
ufw allow 80/tcp
ufw allow 443/tcp
# iptables kullanıyorsanız
iptables -L -n | grep -E "80|443"
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
# Firewalld kullanıyorsanız (CentOS/RHEL)
firewall-cmd --list-all
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
4. Sertifika Yenileme Sessizce Başarısız Oluyor
Bu belki de en tehlikeli senaryo. Certbot’un otomatik yenileme cron job’u veya systemd timer’ı çalışıyor gibi görünüyor ama sertifika gerçekte yenilenmiyor. Bunu fark ettiğinizde sertifikanın süresi dolmuş oluyor.
# Mevcut sertifikaların durumunu kontrol et
certbot certificates
# Yenileme simülasyonu yap (gerçek sertifika değiştirmez)
certbot renew --dry-run
# Yenileme loglarını incele
tail -100 /var/log/letsencrypt/letsencrypt.log
# Systemd timer durumunu kontrol et
systemctl status certbot.timer
systemctl list-timers | grep certbot
# Cron job'u kontrol et
cat /etc/cron.d/certbot
Yenileme loglarında dikkat etmeniz gereken satırlar:
# Başarılı yenileme
grep "Congratulations|renewed|no action taken" /var/log/letsencrypt/letsencrypt.log | tail -20
# Başarısız yenileme denemelerini bul
grep -i "error|failed|exception" /var/log/letsencrypt/letsencrypt.log | tail -30
5. Apache Reload Hatası
Sertifika başarıyla yenileniyor ama Apache’nin yeni sertifikayı yüklemesi için reload yapılmıyor. Bu durumda eski sertifika bellek üzerinde çalışmaya devam eder.
Certbot’un yenileme sonrası hook sistemi bu iş için var. Kontrol edin:
# Mevcut deploy hook'larını listele
ls -la /etc/letsencrypt/renewal-hooks/deploy/
ls -la /etc/letsencrypt/renewal-hooks/post/
ls -la /etc/letsencrypt/renewal-hooks/pre/
# Apache reload hook'u yoksa oluşturun
cat > /etc/letsencrypt/renewal-hooks/deploy/apache-reload.sh << 'EOF'
#!/bin/bash
systemctl reload apache2
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/apache-reload.sh
deploy hook’ları sadece sertifika gerçekten yenilendiğinde çalışır. post hook’ları ise yenileme girişiminden sonra her seferinde çalışır. Çoğu durumda deploy hook’u kullanmak daha doğrudur.
Apache Konfigürasyon Testleri
Sertifika sorunlarının bir kısmı aslında Apache konfigürasyon hatalarından kaynaklanır. Sertifika yüklendikten sonra Apache başlamıyorsa veya yanlış sertifikayı sunuyorsa şu kontrolleri yapın:
# Apache konfigürasyonunu test et
apachectl configtest
# veya
apache2ctl -t
# SSL konfigürasyonunu doğrula
openssl verify -CAfile /etc/letsencrypt/live/example.com/chain.pem
/etc/letsencrypt/live/example.com/cert.pem
# Sunucunun gerçekte hangi sertifikayı sunduğunu kontrol et
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null
| openssl x509 -noout -dates -subject
# Sertifika zincirini doğrula
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -text | grep -A 2 "Issuer"
Apache SSL sanal host konfigürasyonunuzun doğru dosyaları gösterdiğinden emin olun:
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/html
SSLEngine on
# fullchain.pem kullanın, sadece cert.pem değil
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# Eski Apache sürümleri için (2.4.8 öncesi)
# SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
# Modern TLS ayarları
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...
SSLHonorCipherOrder off
</VirtualHost>
Kritik nokta: SSLCertificateFile için her zaman fullchain.pem kullanın. Sadece cert.pem kullandığınızda bazı tarayıcılar ve istemciler sertifika zincirini doğrulayamaz.
Rate Limit Sorunları
Let’s Encrypt’in rate limit’leri gerçek bir sorun olabilir, özellikle test ortamlarında veya sık sık yeniden kurulum yaptığınızda.
Mevcut limitler:
- Certificates per Registered Domain: Haftada 50 sertifika
- Duplicate Certificate: Haftada 5 aynı sertifika
- Failed Validations: Saatte 5 başarısız doğrulama girişimi
Rate limit’e takıldığınızda şu hatayı alırsınız:
Error: urn:ietf:params:acme:error:rateLimited :: There were too many requests of a given type :: Error creating new order :: too many certificates already issued for exact set of domains
# Rate limit durumunu kontrol etmek için
# https://crt.sh adresini kullanabilirsiniz
curl "https://crt.sh/?q=example.com&output=json" | python3 -m json.tool | grep "not_before" | head -20
# Staging ortamını kullanarak test edin (rate limit yok)
certbot certonly --staging -d example.com -d www.example.com
--webroot -w /var/www/letsencrypt
# Staging sertifikasını kaldır ve gerçek sertifika al
certbot delete --cert-name example.com
certbot certonly -d example.com -d www.example.com
--webroot -w /var/www/letsencrypt
Altın kural: Yeni bir konfigürasyonu test ederken her zaman --staging ve --dry-run flag’lerini kullanın. Rate limit’e takılmak ve birkaç gün beklemek zorunda kalmak gereksiz bir acı.
Wildcard Sertifika Sorunları
Apache üzerinde *.example.com gibi wildcard sertifika kullanmak istiyorsanız DNS-01 challenge zorunludur. Bu da DNS sağlayıcınızın API’sini destekleyen bir Certbot plugin’i gerektiriyor.
# DNS plugin'ini yükle (örnek: Cloudflare için)
apt install python3-certbot-dns-cloudflare
# Cloudflare API token dosyasını oluştur
cat > /root/.cloudflare.ini << 'EOF'
dns_cloudflare_api_token = your_api_token_here
EOF
chmod 600 /root/.cloudflare.ini
# Wildcard sertifika al
certbot certonly
--dns-cloudflare
--dns-cloudflare-credentials /root/.cloudflare.ini
-d example.com
-d "*.example.com"
DNS propagasyonu beklemek için --dns-cloudflare-propagation-seconds parametresini artırmanız gerekebilir:
certbot certonly
--dns-cloudflare
--dns-cloudflare-credentials /root/.cloudflare.ini
--dns-cloudflare-propagation-seconds 60
-d example.com
-d "*.example.com"
Sertifika İzleme ve Proaktif Yönetim
Sorunları önceden yakalamak için izleme mekanizmaları kurun. İşte basit ama etkili bir script:
#!/bin/bash
# /usr/local/bin/check-ssl-expiry.sh
DOMAIN=$1
WARN_DAYS=30
CRITICAL_DAYS=14
if [ -z "$DOMAIN" ]; then
echo "Kullanim: $0 domain.com"
exit 1
fi
EXPIRY=$(echo | openssl s_client -connect ${DOMAIN}:443 -servername ${DOMAIN} 2>/dev/null
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$EXPIRY" ]; then
echo "HATA: ${DOMAIN} adresine baglanilamiyor veya sertifika alinamiyor"
exit 2
fi
EXPIRY_EPOCH=$(date -d "${EXPIRY}" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "${DOMAIN}: Sertifika ${DAYS_LEFT} gun sonra sona eriyor (${EXPIRY})"
if [ ${DAYS_LEFT} -lt ${CRITICAL_DAYS} ]; then
echo "KRITIK: Hemen yenileyin!"
exit 2
elif [ ${DAYS_LEFT} -lt ${WARN_DAYS} ]; then
echo "UYARI: Sertifika yakinda sona erecek"
exit 1
else
echo "OK: Sertifika gecerli"
exit 0
fi
Bu scripti cron’a ekleyin ve e-posta bildirimi ayarlayın:
chmod +x /usr/local/bin/check-ssl-expiry.sh
# Crontab'a ekle
echo "0 9 * * * root /usr/local/bin/check-ssl-expiry.sh example.com | mail -s 'SSL Check' [email protected]"
>> /etc/cron.d/ssl-check
Gerçek Dünya Senaryosu: Yenileme Sessizce Başarısız Oldu
Geçen ay bir müşterinin sunucusunda tam bu durum yaşandı. Apache çalışıyordu, site erişilebilirdi ama sertifika 2 gün önce dolmuştu. Certbot’un systemd timer’ı çalışıyordu ama yenileme başarısız oluyordu. Sorunun kaynağını bulmak için attığımız adımlar:
# 1. Adım: Sertifika durumunu kontrol et
certbot certificates
# Çıktı: EXPIRED olarak görünüyordu
# 2. Adım: Son yenileme logunu incele
tail -200 /var/log/letsencrypt/letsencrypt.log | grep -A 5 "renewal"
# 3. Adım: Dry-run ile test et
certbot renew --dry-run 2>&1 | tee /tmp/certbot-debug.log
cat /tmp/certbot-debug.log
# 4. Adım: Sorunu bulduk - .htaccess dosyası challenge yolunu engelliyordu
cat /var/www/html/.htaccess | grep -i "deny|redirect|rewrite"
.htaccess dosyasında şöyle bir satır vardı:
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L,NE]
Bu rewrite kuralı koşulsuz olarak her şeyi HTTPS’e yönlendiriyordu. Düzeltme:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/.well-known/
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L,NE]
Bu değişiklikten sonra certbot renew --dry-run başarıyla tamamlandı ve sertifika hemen yenilendi.
Faydalı Diagnostic Komutları Özeti
Herhangi bir sorunla karşılaştığınızda sırayla çalıştırabileceğiniz kontrol listesi:
# 1. Certbot sürümü ve kurulum durumu
certbot --version
which certbot
# 2. Mevcut sertifikaların listesi ve geçerlilik süresi
certbot certificates
# 3. Renewal konfigürasyon dosyalarını incele
ls /etc/letsencrypt/renewal/
cat /etc/letsencrypt/renewal/example.com.conf
# 4. Dry-run ile yenileme testi
certbot renew --dry-run --cert-name example.com
# 5. Apache'nin sertifika dosyalarını okuduğunu doğrula
apache2ctl -t -D DUMP_VHOSTS
# 6. TLS bağlantısını dışarıdan test et
curl -vI https://example.com 2>&1 | grep -E "SSL|TLS|certificate|expire"
# 7. Sertifika detaylarını görüntüle
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -text
| grep -E "Subject|Issuer|Not Before|Not After|DNS"
Sonuç
Let’s Encrypt ve Apache kombinasyonu, doğru kurulduğunda neredeyse hiç bakım gerektirmez. Sorunların büyük çoğunluğu birkaç ana kategoride toplanır: HTTP-01 challenge’ın engellenmiş olması, yenileme sonrası Apache’nin reload edilmemesi ve sessiz başarısızlıkların fark edilmemesi.
En önemli önerilerim şunlardır: Her şeyden önce staging ortamında test edin ve rate limit’e takılmayın. Sertifika yenileme hook’larınızın doğru kurulduğundan emin olun ve bir izleme mekanizması kurun. Sertifika dolmadan en az 30 gün önce uyarı almanız, panik içinde iş yapmak zorunda kalmanızı engeller.
Certbot logları /var/log/letsencrypt/letsencrypt.log dosyasında oldukça detaylıdır. Bir sorunla karşılaştığınızda bu logu okumaktan kaçınmayın, çoğu zaman tam olarak neyin yanlış gittiğini söyler. Deneyimlerime göre sorunu logda bulmak, çözmekten daha uzun sürüyor, o yüzden log okuma alışkanlığı edinmek uzun vadede çok büyük zaman kazandırıyor.
