Cumartesi sabahı telefonunuz titriyor: müşteri "site açılmıyor, tarayıcı güvenli değil diyor" yazmış. SSH ile bağlanıp certbot renew --dry-run çalıştırıyorsunuz, çıktının ortasında Timeout during connect (likely firewall problem) satırını görüyorsunuz. Üç ay önce kurduğunuz cron görevi hâlâ çalışıyor ama nginx'in .well-known/acme-challenge dizinini eski bir location bloğu yutmuş, doğrulama düşmüş, kimse fark etmemiş. Sertifika sessizce süresi dolana kadar bekledikten sonra tam hafta sonunda patlamış.
Bu senaryoyu bir kez yaşadıysanız Caddy'nin ne çözdüğünü anlatmak kolaylaşır. Caddy, Go ile yazılmış açık kaynak bir web sunucusudur ve ACME istemcisi sunucunun kendi içindedir. Ayrı bir certbot paketi, ayrı bir cron/timer, ayrı bir webroot dizini, yenileme sonrası systemctl reload nginx kancası yoktur. Bir alan adını yapılandırma dosyasına yazdığınız anda Caddy sertifikayı alır, kurar, süresi dolmadan yeniler ve yenilediğinde yapılandırmayı kendi içinde tazeler.
Bu rehberde Caddy'yi Ubuntu/Debian üzerine kurup üç farklı senaryoda çalıştıracağız: statik site, self-hosted bir uygulamanın önünde reverse proxy ve PHP-FPM ile klasik bir PHP sitesi. Ardından işin sıkıcı ama kritik tarafına geleceğiz — sunucuda zaten nginx varken 80/443 çakışmasını nasıl yöneteceğiniz, sertifika alınamadığında nereye bakacağınız ve hangi durumlarda Caddy'ye geçmemeniz gerektiği.
Caddy Sertifikayı Neden Kendi Alıyor?#
Let's Encrypt'ten sertifika almak üç adımlık bir iştir: ACME sunucusuna hesap açmak, alan adının size ait olduğunu bir doğrulama (challenge) ile kanıtlamak ve imzalanmış sertifikayı diske yazmak. Certbot bu üç adımı yapar ve orada durur. Sertifikayı web sunucusuna tanıtmak, yenilemeyi zamanlamak ve yenileme sonrası servisi yeniden yüklemek sizin sorumluluğunuzdadır. Zincirdeki her halka ayrı bir arıza noktasıdır.
Caddy'de bu zincir yoktur çünkü ACME istemcisi ile TLS'i sonlandıran katman aynı süreçtir. Yapılandırmada ornek.com gördüğü anda:
- Bellekte bir ACME hesabı yoksa oluşturur ve
/var/lib/caddyaltına yazar. - HTTP-01 (port 80) veya TLS-ALPN-01 (port 443) doğrulamasını kendi dinlediği port üzerinden yanıtlar — dosya sistemine bir doğrulama dosyası bırakmaz, dolayısıyla
locationkuralı bir şeyi yutamaz. - Sertifikayı alıp bellekte tutar ve diske yedekler.
- Ömrünün üçte ikisi dolduğunda arka planda yeniler; yenilenen sertifika yeniden başlatma olmadan devreye girer.
Doğrulama mantığının kendisini merak ediyorsanız Let's Encrypt ücretsiz SSL yazısı arka plandaki ACME akışını ayrıntılı anlatıyor. Buradaki fark araç değil, mimari: yenileme ayrı bir görev değil, sunucunun normal işleyişinin parçası.
Varsayılan olarak Caddy sertifika sağlayıcısı olarak Let's Encrypt'i kullanır ve başarısız olursa yedek bir ACME sağlayıcısına (ZeroSSL) düşer. Bu davranış sürüme göre değişebildiği için üretimde hangi sağlayıcının kullanıldığını günlüklerden doğrulamak iyi bir alışkanlıktır.
Ubuntu ve Debian Üzerine Caddy Kurulumu#
Caddy'nin resmi APT deposu Cloudsmith üzerinde barındırılır. Dağıtımın kendi deposundaki paket çoğu zaman geride kalır, bu yüzden resmi depoyu eklemek daha sağlıklıdır:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
Kurulumdan sonra caddy adında bir sistem kullanıcısı, caddy.service adında bir systemd birimi ve /etc/caddy/Caddyfile adında örnek bir yapılandırma oluşur. Doğrulayın:
caddy version
systemctl status caddy --no-pager
Servis ayakta ise sunucunun IP'sine tarayıcıdan girdiğinizde Caddy'nin karşılama sayfasını görürsünüz. Sertifika alınabilmesi için 80 ve 443 portlarının dışarıya açık olması şart; güvenlik duvarı katmanınızı UFW güvenlik duvarı yazısındaki gibi yapılandırdıysanız iki portu da geçirdiğinizden emin olun:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
Alan adının A kaydı sunucunun IP'sini göstermiyorsa Caddy sertifika alamaz ve döngüye girer. DNS'i yayılmadan Caddy'yi canlıya almayın.
İlk Caddyfile: Statik Site Üç Satırda#
/etc/caddy/Caddyfile dosyasını açıp içeriğini şununla değiştirin:
{
email [email protected]
}
ornek.com, www.ornek.com {
root * /var/www/ornek.com
encode zstd gzip
file_server
}
Üstteki süslü parantez bloğu global seçeneklerdir; buradaki email ACME hesabına bağlanır ve sertifika süresi dolmak üzereyken Let's Encrypt'in size e-posta atabilmesini sağlar. Alttaki blok ise bir "site adresi"dir. Şema (https://) yazmadığınız her alan adı için Caddy otomatik HTTPS'i açar: sertifikayı alır, 80 portundan gelen isteği 443'e yönlendirir ve HTTP/2 ile HTTP/3'ü etkinleştirir. https-yonlendirme için ayrı bir kural yazmanız gerekmez — HTTP'den HTTPS'e yönlendirme tarafında elle kurduğunuz return 301 bloğunun karşılığı burada varsayılan davranıştır.
Uygulamadan önce her seferinde iki komut çalıştırma alışkanlığı edinin:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
caddy fmt girintileri düzeltir, caddy validate yapılandırmayı ayrıştırıp hata varsa satır numarasıyla söyler. reload ise servisi kesintiye uğratmadan yeni yapılandırmaya geçer.
Ne olup bittiğini izlemek için:
sudo journalctl -u caddy --no-pager -n 80
İlk sertifika alımı başarılıysa günlükte certificate obtained successfully benzeri bir satır görürsünüz. Sertifikaların diskteki yeri:
sudo ls -R /var/lib/caddy/.local/share/caddy/certificates/
Reverse Proxy: Self-Hosted Uygulamanın Önüne Caddy Koymak#
Caddy'nin en çok kullanıldığı senaryo bu. Docker ile ayağa kaldırdığınız bir uygulama 3000, 5678 veya 8080 portunda dinliyor; siz onu alan adı ve sertifika ile dışarı açmak istiyorsunuz:
panel.ornek.com {
reverse_proxy 127.0.0.1:3000
}
otomasyon.ornek.com {
reverse_proxy 127.0.0.1:5678
}
İki blok, iki alan adı, iki sertifika. Caddy X-Forwarded-For, X-Forwarded-Proto ve X-Forwarded-Host başlıklarını kendiliğinden ekler; WebSocket bağlantılarını da ek yapılandırma olmadan geçirir. Nginx tarafında proxy_http_version 1.1, Upgrade ve Connection başlıklarını elle yazmak zorunda kaldığınız kısım burada yoktur. Nginx karşılığını görmek isterseniz nginx reverse proxy yapılandırma yazısındaki blokla bu üç satırı yan yana koymak farkı anlatmaya yetiyor.
Yük dengeleme de aynı direktifin içindedir:
api.ornek.com {
reverse_proxy 10.0.0.11:8080 10.0.0.12:8080 {
lb_policy least_conn
health_uri /health
health_interval 10s
}
}
Uygulamanızı Docker Compose ile çalıştırıyorsanız konteyner portlarını 127.0.0.1 üzerine bağlayıp (örneğin 127.0.0.1:3000:3000) dışarıya sadece Caddy'yi açmak en temiz kurulumdur. Böylece uygulama portu internete hiç çıkmaz.
Yol Bazlı Yönlendirme#
Tek alan adı altında birden çok servis varsa:
ornek.com {
handle /api/* {
reverse_proxy 127.0.0.1:4000
}
handle {
root * /var/www/ornek.com
file_server
}
}
handle blokları sırayla değerlendirilir ve ilk eşleşen kazanır; sondaki boş handle "diğer her şey" anlamına gelir.
PHP Sitesini Caddy ile Yayınlamak#
PHP tarafı da tek direktife iner. Önce PHP-FPM'in soket yolunu doğrulayın:
sudo systemctl status php8.3-fpm --no-pager
ls /run/php/
Ardından:
ornek.com {
root * /var/www/ornek.com/public
encode zstd gzip
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
log {
output file /var/log/caddy/ornek.com.log
format json
}
}
Soket yolundaki çift eğik çizgi yazım hatası değildir: unix/ şema önekidir, ardından gelen /run/php/... mutlak yoldur. Tek eğik çizgi yazarsanız Caddy soketi bulamaz ve 502 döner.
php_fastcgi direktifi tek başına üç işi birden yapar: dizin isteklerinde index.php dener, var olmayan yolları index.php'ye yönlendirir (WordPress ve Laravel gibi ön denetleyici kullanan uygulamalar için gereken davranış) ve FastCGI parametrelerini doğru kurar. Nginx'te bu, try_files $uri $uri/ /index.php?$args satırı artı bir location ~ \.php$ bloğu artı fastcgi_params dahil etmek demektir.
Günlük dizinini oluşturmayı unutmayın:
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy
sudo systemctl reload caddy
Nginx + Certbot ile Caddy Arasındaki Fark Nerede Ortaya Çıkıyor?#
İki yaklaşımı adil karşılaştırmak için aynı işi yapmak için ne kadar parça gerektiğine bakalım:
| Konu | Nginx + Certbot | Caddy |
|---|---|---|
| Sertifika alma | certbot --nginx ayrı çalıştırılır | Alan adı yazıldığı anda otomatik |
| Yenileme | systemd timer veya cron + --deploy-hook | Sunucu süreci içinde, zamanlayıcısız |
| Yenileme sonrası reload | Kancayla elle kurulur | Gerekmez, bellekte tazelenir |
| HTTP → HTTPS | Ayrı server bloğu + return 301 | Varsayılan |
| Bir statik site | ~15-20 satır (iki blok) | 4 satır |
| Basit reverse proxy | 6-8 satır (proxy başlıkları dahil) | 1 satır |
| HTTP/3 | Derleme/sürüm ve ek direktif ister | Varsayılan açık |
| Yapılandırma sözdizimi | Blok tabanlı, bağlam kuralları öğrenilir | Site adresi + direktif |
| Karmaşık kurallar (map, geo, limit_req) | Çok olgun, geniş ekosistem | Daha sınırlı, eklenti gerekebilir |
| Ekip bilgisi ve internet kaynağı | Devasa | Görece küçük |
Tablo Caddy'yi kayıtsız şartsız kazanan gibi gösteriyor ama son iki satır bilerek orada. Certbot'un ileri kullanımına, kancalara ve DNS doğrulamasına zaten hâkimseniz certbot ileri kullanım tarafındaki otomasyonunuz gayet iyi çalışıyor demektir ve göç etmenin somut bir getirisi olmayabilir.
Sunucuda Zaten Nginx Varken 80/443 Çakışması#
En sık karşılaşılan hata budur: Caddy kurulur, systemctl start caddy çalıştırılır ve servis şu satırla ölür:
listen tcp :443: bind: address already in use
Sebep basit — 80 ve 443'ü aynı anda iki süreç dinleyemez. Önce kimin dinlediğini görün:
sudo ss -tlnp | grep -E ':80 |:443 '
Buradan sonra üç seçeneğiniz var ve ikisi tuzaktır.
Seçenek 1 — Nginx'i durdurup portları Caddy'ye devretmek. Temiz olan yol budur, ancak nginx'in altındaki tüm vhost'ları Caddyfile'a taşımanız gerekir. Geçişi kademeli yapmak isterseniz önce nginx'i durdurun, Caddy ile tek alan adını test edin, sonra kalanları taşıyın:
sudo systemctl stop nginx
sudo systemctl disable nginx
sudo systemctl restart caddy
Seçenek 2 — Caddy'yi alternatif portlarda çalıştırmak. Test amaçlı geçerlidir, üretim için değil:
{
http_port 8080
https_port 8443
}
Bunu yaptığınızda HTTP-01 ve TLS-ALPN-01 doğrulamaları çalışmaz, çünkü Let's Encrypt doğrulama isteğini her zaman 80 veya 443'e gönderir. Bu yapılandırmayla sertifika almak istiyorsanız DNS-01 doğrulamasına geçmek zorundasınız (aşağıda).
Seçenek 3 — Nginx'i önde bırakıp Caddy'yi arkaya koymak. Teknik olarak mümkündür ama otomatik HTTPS'in tüm anlamını yok eder: TLS'i nginx sonlandırır, sertifikayı yine certbot alır, Caddy sadece bir iç proxy'ye dönüşür. Bir geçiş dönemi çözümü olarak birkaç gün taşınabilir, kalıcı mimari olarak taşınmamalıdır.
DNS-01 Doğrulaması ve Wildcard Sertifika#
Portlar müsait değilse veya *.ornek.com gibi bir wildcard sertifika istiyorsanız DNS doğrulaması gerekir. Bunun için Caddy'ye DNS sağlayıcı eklentisi eklemek şarttır; varsayılan binary bu yeteneği içermez:
sudo caddy add-package github.com/caddy-dns/cloudflare
sudo systemctl restart caddy
API anahtarını yapılandırma dosyasına düz metin yazmak yerine servis ortamına verin:
sudo systemctl edit caddy
Açılan dosyaya:
[Service]
Environment="CF_API_TOKEN=buraya_token"
Ardından Caddyfile:
*.ornek.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
reverse_proxy 127.0.0.1:3000
}
Sertifika Alınamıyor: En Sık Görülen Beş Sebep#
Günlükte could not get certificate veya authorization failed görüyorsanız sırayla şunları eleyin.
Belirti: Timeout during connect → Sebep: 80 portu dışarıdan kapalı. → Çözüm: Güvenlik duvarı ve sağlayıcı tarafındaki ağ kurallarını kontrol edin; Caddy'nin 80'i dinlediğini ss -tlnp ile doğrulayın.
Belirti: DNS problem: NXDOMAIN → Sebep: A kaydı yok veya henüz yayılmamış. → Çözüm: dig ornek.com A +short çıktısının sunucu IP'sini verdiğinden emin olun, sonra yeniden deneyin.
Belirti: too many certificates already issued → Sebep: Let's Encrypt oran sınırına takıldınız (aynı kayıtlı alan adı için haftalık sertifika limiti). → Çözüm: Deneme yaparken mutlaka staging sunucusunu kullanın; staging'in oran sınırı çok daha geniştir.
{
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
Test bittiğinde bu satırı silin ve sudo rm -rf /var/lib/caddy/.local/share/caddy/certificates ile staging sertifikalarını temizleyip yeniden yükleyin; aksi hâlde tarayıcılar staging sertifikasına güvenmediği için site "güvenli değil" görünür.
Belirti: Sertifika alınıyor ama site iç ağdan açılmıyor → Sebep: Alan adı sunucunun kendisine değil bir CDN'e (Cloudflare proxy) çözümleniyor ve doğrulama CDN üzerinden geliyor. → Çözüm: İlk sertifika alımı sırasında turuncu bulutu kapatın veya doğrudan DNS-01'e geçin.
Belirti: Yerel geliştirme ortamında sürekli hata → Sebep: localhost veya .test gibi genel olmayan bir isim için Let's Encrypt sertifika veremez. → Çözüm: tls internal ile Caddy'nin kendi iç CA'sını kullanın:
uygulama.test {
tls internal
reverse_proxy 127.0.0.1:8080
}
Caddy'ye Ne Zaman Geçmemelisiniz?#
Otomasyon güzeldir ama her ortama uymaz. Şu durumlarda mevcut kurulumunuzda kalın:
- cPanel, Plesk veya DirectAdmin kullanıyorsanız. Panel Apache/LiteSpeed yapılandırmasını kendisi üretir ve 80/443'ü kendisi yönetir. Araya Caddy sokmak panelin ürettiği vhost'ları, e-posta doğrulama yollarını ve panelin kendi SSL otomasyonunu bozar. Panelli ortamda doğru araç panelin kendi SSL modülüdür.
- Nginx yapılandırmanız gerçekten karmaşıksa.
map,geo,limit_reqzincirleri, ayrıntılı önbellek katmanı veya Lua modülleri kullanıyorsanız bunların birebir karşılığını Caddy'de kurmak zaman alır ve bazıları eklenti gerektirir. Kazanacağınız şey sadece sertifika otomasyonuysa bu takas kötüdür. - Kurumsal OV/EV sertifikası kullanıyorsanız. Sertifika elle satın alınıp elle kuruluyorsa Caddy'nin en büyük avantajı devre dışı kalır;
tls sertifika.crt sertifika.keyile kurabilirsiniz ama bunun için sunucu değiştirmenin anlamı yoktur. - Ekip nginx biliyor ve gece nöbeti tutan siz değilseniz. Bir arıza anında yapılandırmayı okuyup düzeltecek kişinin dile hâkim olması, satır sayısından daha değerlidir.
Buna karşılık Caddy'yi tereddütsüz seçebileceğiniz yerler de nettir: tek amaçlı bir uygulama sunucusu, self-hosted araçların önündeki proxy katmanı, çok sayıda alt alan adının olduğu iç servisler ve sertifika yenilemesini takip edecek kimsenin olmadığı küçük projeler. Nginx ile Caddy arasındaki tercihi genel mimari açısından tartan bir bakış için Apache ve Nginx karşılaştırması yazısındaki değerlendirme ölçütleri burada da işinize yarar.
Sıkça Sorulan Sorular#
Caddy sertifikayı ne sıklıkla yeniliyor ve ben bir şey yapmam gerekiyor mu?#
Caddy sertifikaların geçerlilik durumunu arka planda düzenli olarak kontrol eder ve ömrünün yaklaşık üçte ikisi dolduğunda yenilemeyi başlatır. Doksan günlük bir Let's Encrypt sertifikasında bu, süre dolmadan yaklaşık bir ay önce demektir. Yenilenen sertifika servis yeniden başlatılmadan devreye girer. Sizin tarafınızda cron görevi, systemd timer veya reload kancası kurmanız gerekmez.
Mevcut nginx yapılandırmamı Caddy'ye otomatik çevirebilir miyim?#
Caddy'nin nginx yapılandırmasını içe aktarabilen deneysel bir dönüştürücüsü vardır, ancak üretim yapılandırmalarını olduğu gibi taşımaya güvenmeyin. Karmaşık rewrite, map ve önbellek kuralları çoğunlukla elle yeniden yazılır. Sağlıklı yöntem, en önemli tek siteyi elle Caddyfile'a çevirip staging ortamında doğrulamak, sonra kalanları teker teker taşımaktır.
Caddy nginx'ten yavaş mı?#
Statik dosya ve reverse proxy senaryolarında ikisi arasındaki fark çoğu site için ölçülebilir ama hissedilmez düzeydedir; darboğaz genellikle uygulama veya veritabanı tarafındadır. Çok yüksek eşzamanlı bağlantı ve saniyede on binlerce istek gibi uç senaryolarda nginx'in olgun ayar seçenekleri hâlâ avantajlıdır. Küçük ve orta ölçekli projelerde performans, tercih ederken bakacağınız ilk kriter olmamalı.
80 portunu kapatıp yalnızca 443 kullanabilir miyim?#
Kullanabilirsiniz ama HTTP-01 doğrulaması çalışmaz. Bu durumda Caddy TLS-ALPN-01 doğrulamasını 443 üzerinden deneyebilir; yine de en güvenli yol DNS-01 doğrulamasına geçmektir. Ayrıca 80'i tamamen kapatmak, adresi http:// ile yazan ziyaretçilerin siteye hiç ulaşamaması anlamına gelir — yönlendirme yapılabilmesi için 80'in açık olması pratikte tercih edilir.
Caddy'yi Docker içinde çalıştırırken sertifikalar kayboluyor, neden?#
Sertifikalar konteyner içinde /data dizininde saklanır. Bu dizini kalıcı bir volume'e bağlamazsanız her konteyner yeniden oluşturmada Caddy sertifikayı sıfırdan almaya çalışır ve kısa sürede Let's Encrypt oran sınırına takılırsınız. Compose dosyanızda /data ve tercihen /config dizinlerini adlandırılmış volume'lere bağlayın.
Aynı sunucuda hem Apache hem Caddy çalıştırabilir miyim?#
İkisinin aynı anda 80 ve 443'ü dinlemesi mümkün değildir. Ancak Apache'yi 8080 gibi bir iç porta alıp Caddy'yi öne koyabilirsiniz: Caddy TLS'i sonlandırır ve reverse_proxy 127.0.0.1:8080 ile istekleri Apache'ye devreder. Bu, .htaccess bağımlılığı olan eski uygulamaları taşımadan HTTPS otomasyonu kazanmanın pratik yoludur.