Web sunucusu yönetiminde en sık karşılaşılan ihtiyaçlardan biri URL yönetimidir. Eski URL’leri yenilere yönlendirmek, SEO dostu adresler oluşturmak, belirli path’leri farklı backend’lere iletmek… Bunların hepsini Caddy ile oldukça temiz bir şekilde halledebilirsin. Nginx veya Apache’deki gibi karmaşık regex zincirleri yazmak zorunda kalmadan, okunabilir bir syntax ile güçlü yönlendirme kuralları tanımlayabilirsin. Bu yazıda Caddy’nin rewrite ve redir direktiflerini gerçek dünya senaryolarıyla birlikte ele alacağız.
Temel Kavramlar: Rewrite mi, Redirect mi?
Bu iki kavramı karıştırmak çok yaygın bir durum. Aralarındaki farkı net olarak anlamak, doğru direktifi kullanman açısından kritik.
Redirect (redir): Tarayıcıya “bu URL artık burada değil, şu adrese git” der. Tarayıcı yeni bir HTTP isteği yapar. URL bar’da değişiklik görürsün. 301 (kalıcı) veya 302 (geçici) status kodu döner. SEO açısından önemli.
Rewrite: Sunucu tarafında iç bir işlemdir. İstek farklı bir path’e yeniden yönlendirilir ama tarayıcı bundan haberdar olmaz. URL bar’da değişiklik görmezsin. Proxy’lere istek göndermeden önce path düzenlemek için idealdir.
Caddy’de bu iki işlemi sırasıyla redir ve rewrite direktifleriyle yapıyoruz.
Basit Redirect Örnekleri
En temel kullanım senaryosundan başlayalım. Diyelim ki eski bir sayfanı yeni bir adrese taşıdın.
# Caddyfile - Basit 301 redirect
example.com {
redir /eski-sayfa /yeni-sayfa 301
redir /hakkimizda /kurumsal/hakkimizda 301
redir /iletisim /bize-ulasin 302
root * /var/www/html
file_server
}
Burada dikkat etmen gereken nokta: 301 kalıcı yönlendirme, 302 ise geçici yönlendirme. Eğer sayfa gerçekten taşındıysa ve geri dönmeyecekse 301 kullan. Test aşamasında veya geçici değişiklikler için 302 daha uygun.
HTTP’den HTTPS’e yönlendirme ise Caddy’de otomatik gelir ama manuel olarak da tanımlayabilirsin:
# HTTP'den HTTPS'e yönlendirme
http://example.com {
redir https://example.com{uri} 301
}
https://example.com {
root * /var/www/html
file_server
}
{uri} placeholder’ı burada çok önemli. Path ve query string dahil tüm URI’yi olduğu gibi aktarıyor. Bunu kullanmazsan kullanıcı her zaman ana sayfaya yönlendirilir.
www ile www-siz Yönlendirme
SEO için domain konsolidasyonu şart. Ya www varsa www-siz’e, ya da tersi. İkisinin birden aktif olması duplicate content sorununa yol açar.
# www olmadan ana domain'e yönlendirme
www.example.com {
redir https://example.com{uri} 301
}
example.com {
root * /var/www/html
file_server
encode gzip
}
Tersi de gerekebilir, bazı kurulumlarda www tercih edilir:
# Ana domain'den www'ye yönlendirme
example.com {
redir https://www.example.com{uri} 301
}
www.example.com {
root * /var/www/html
file_server
encode gzip
}
Rewrite ile URL Dönüşümleri
Rewrite’ın en güzel kullanım alanlarından biri, temiz URL’leri arka tarafta çalışan bir sisteme aktarmaktır. Örneğin PHP uygulamaları çoğunlukla index.php üzerinden çalışır ama URL’de bunu göstermek istemezsin.
# PHP uygulaması için rewrite
example.com {
root * /var/www/myapp
# Tüm istekleri index.php'ye yönlendir (dosya yoksa)
rewrite * /index.php
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
}
Ama bu çok agresif bir yaklaşım. Statik dosyaları da index.php’ye gönderir. Daha akıllıca bir kullanım:
# Sadece bulunamayan dosyaları index.php'ye yönlendir
example.com {
root * /var/www/myapp
# Önce dosya olup olmadığına bak, yoksa rewrite et
@notStatic {
not file
not path /images/* /css/* /js/* /fonts/*
}
rewrite @notStatic /index.php
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
}
Bu pattern Laravel, Symfony gibi framework’lerde çok işe yarar.
Named Matchers ile Gelişmiş Kurallar
Caddy’nin en güçlü özelliklerinden biri named matcher sistemi. @ işaretiyle bir matcher tanımlayıp sonra istediğin yerde kullanabilirsin.
example.com {
# Belirli path prefix'i için matcher
@admin path /admin/*
@api path /api/*
@legacy path /old/* /eski/* /gecmis/*
# Admin'i yeni panel'e yönlendir
redir @admin /panel{path} 301
# API versiyonu geçişi
redir @api https://api.example.com{uri} 301
# Legacy URL'leri temizle
redir @legacy / 301
root * /var/www/html
file_server
}
Matcher’lar içinde birden fazla koşul kombinleyebilirsin:
example.com {
# Method ve path kombinasyonu
@postRequests {
method POST
path /form/*
}
# Header kontrolü ile rewrite
@mobileUsers {
header User-Agent *Mobile*
path /
}
rewrite @mobileUsers /mobile/index.html
root * /var/www/html
file_server
}
Regex ile Güçlü Pattern Matching
Bazen basit path eşleştirme yetmez, regex kullanman gerekir. Caddy’de path_regexp ile bunu yapabilirsin.
example.com {
# Tarih bazlı URL dönüşümü
# /blog/2023/12/25/baslik -> /blog/baslik
@blogOldFormat path_regexp blog_date ^/blog/(d{4})/(d{2})/(d{2})/(.+)$
rewrite @blogOldFormat /blog/{re.blog_date.4}
root * /var/www/html
file_server
}
Yakalanan gruplar {re.matcher_name.index} formatında kullanılıyor. Index 0 tüm eşleşmeyi, 1’den itibaren capture group’ları temsil ediyor.
Daha gerçekçi bir örnek, ürün URL’lerini düzenlemek için:
example.com {
# /urun/12345-ürün-adı formatını /products/12345 formatına çevir
@productUrl path_regexp product ^/urun/(d+)-(.+)$
rewrite @productUrl /products/{re.product.1}
# Eski kategori URL yapısını yeniye yönlendir
@oldCategory path_regexp old_cat ^/kategori-(d+)/(.*)$
redir @oldCategory /kategori/{re.old_cat.2}?cat_id={re.old_cat.1} 301
reverse_proxy localhost:3000
}
Reverse Proxy ile Rewrite Kombinasyonu
Microservice mimarisinde veya birden fazla backend servise sahip olduğunda, rewrite ve reverse_proxy kombinasyonu hayat kurtarır.
example.com {
# /api/v1/* isteklerini backend'e yönlendir, path'i düzenle
@apiV1 path /api/v1/*
handle @apiV1 {
rewrite * /api{path}
reverse_proxy localhost:8080
}
# /static/* isteklerini CDN'e yönlendir
@staticFiles path /static/*
redir @staticFiles https://cdn.example.com{path} 302
# Geriye kalan her şey ana uygulama
handle {
reverse_proxy localhost:3000
}
}
handle direktifi burada çok önemli. Matcher eşleşince o blok çalışır ve diğer handle blokları atlanır. Bu sayede birbirini ezip geçme sorununu önlersin.
Daha karmaşık bir microservice senaryosu:
api.example.com {
# Kullanıcı servisi
@users path /users/* /user/*
handle @users {
rewrite * /api{path}
reverse_proxy user-service:8001
}
# Sipariş servisi
@orders path /orders/* /order/*
handle @orders {
rewrite * /api{path}
reverse_proxy order-service:8002
}
# Ödeme servisi - sadece HTTPS, ekstra header
@payment path /payment/*
handle @payment {
rewrite * /v2{path}
header X-Forwarded-Proto https
reverse_proxy payment-service:8003
}
# Bilinmeyen endpoint
handle {
respond "Not Found" 404
}
}
Query String Manipülasyonu
URL’deki query parametrelerini yönetmek de sık ihtiyaç duyulan bir şey. Caddy bunu birkaç farklı şekilde destekliyor.
example.com {
# Query string içeren eski URL'leri temiz URL'lere çevir
# /sayfa.php?id=123 -> /sayfa/123
@oldQueryFormat {
path /sayfa.php
query id=*
}
redir @oldQueryFormat /sayfa/{query.id} 301
# UTM parametrelerini kaldır (temiz URL'e yönlendir)
@hasUtm query utm_source=*
redir @hasUtm {path} 301
root * /var/www/html
file_server
}
Query string eşleştirmesi query matcher ile yapılıyor. Wildcard destekliyor yani değerin ne olduğu önemli değil, parametrenin varlığı yeterli.
Koşullu Yönlendirme Senaryoları
Gerçek dünyada bazen birden fazla koşulun aynı anda sağlanmasını istiyorsun. Caddy’nin matcher sistemi bunu AND ve NOT mantığıyla destekliyor.
example.com {
# Sadece GET değilse ve /api path'indeyse
@nonGetApi {
not method GET HEAD
path /api/*
}
redir @nonGetApi /api/docs 405
# IP bazlı kısıtlama ile yönlendirme
@notAllowed {
not remote_ip 192.168.1.0/24 10.0.0.0/8
path /internal/*
}
redir @notAllowed /login?returnUrl={uri} 302
root * /var/www/html
file_server
}
Maintenance modu için de bu pattern çok kullanışlı:
example.com {
# Maintenance sayfası aktifken tüm trafiği yönlendir
# Ama /maintenance.html ve statik dosyalar hariç
@maintenance {
not path /maintenance.html /css/* /images/*
file /var/www/html/maintenance.lock
}
redir @maintenance /maintenance.html 302
root * /var/www/html
file_server
}
/var/www/html/maintenance.lock dosyası varsa maintenance modu aktif olur. Dosyayı silince otomatik normale döner. Güzel değil mi?
Çoklu Domain Yönetimi
Birden fazla domain’i tek bir Caddy instance’ında yönetirken yönlendirme kuralları daha da kritik hale gelir.
# Eski domain'den yeni domain'e taşınma
eskidomain.com {
redir https://yenidomain.com{uri} 301
}
# Tüm subdomain'leri tek bir yere yönlendir
*.eskidomain.com {
redir https://yenidomain.com{uri} 301
}
# Yeni domain ana yapı
yenidomain.com {
root * /var/www/yeni
file_server
encode gzip
}
# Alt domain'ler
blog.yenidomain.com {
reverse_proxy localhost:2368
}
shop.yenidomain.com {
reverse_proxy localhost:8080
}
Wildcard subdomain yönlendirmesi için Caddy’nin DNS provider plugin’ine ihtiyaç duyabileceğini unutma. Wildcard TLS sertifikası için DNS challenge gerekiyor.
Caddyfile Snippet’leri ile Tekrar Kullanılabilir Kurallar
Aynı yönlendirme kurallarını birden fazla site için kullanıyorsan, snippet’ler işini kolaylaştırır.
# Ortak güvenlik yönlendirmeleri
(security_redirects) {
# .env ve hassas dosyalara erişimi engelle
@sensitiveFiles path /.env* /*.sql /*.bak /wp-config.php
redir @sensitiveFiles / 301
# .git klasörüne erişimi engelle
@gitDir path /.git/*
respond @gitDir "Forbidden" 403
}
# SEO standartları
(seo_rules) {
# Trailing slash tutarlılığı
@trailingSlash {
path_regexp ^(.+)/$
not path /
}
redir @trailingSlash {re.1} 301
}
# Site tanımlarında kullan
example.com {
import security_redirects
import seo_rules
root * /var/www/html
file_server
}
other-site.com {
import security_redirects
import seo_rules
root * /var/www/other
file_server
}
Hata Ayıklama ve Test
Yönlendirme kurallarını test etmek için birkaç pratik yöntem var.
curl ile redirect chain’ini takip edebilirsin:
# Redirect'i takip et ve tüm adımları göster
curl -v -L http://example.com/eski-sayfa
# Sadece status kodlarını ve Location header'ını gör
curl -sI http://example.com/eski-sayfa | grep -E "HTTP|Location"
# Tüm redirect zincirini adım adım gör
curl -sIL http://example.com/eski-sayfa
# Caddy log'larını canlı izle
tail -f /var/log/caddy/access.log | grep -E "301|302|rewrite"
Caddy’nin config’ini validate etmek için:
# Syntax kontrolü
caddy validate --config /etc/caddy/Caddyfile
# Config'i reload et (restart gerekmez)
caddy reload --config /etc/caddy/Caddyfile
# Mevcut config'i görüntüle
caddy adapt --config /etc/caddy/Caddyfile --pretty
Özellikle regex tabanlı rewrite’larda hata ayıklamak zor olabilir. Caddy’nin log direktifini verbose modda açarak detaylı bilgi alabilirsin:
{
debug
}
example.com {
log {
level DEBUG
output file /var/log/caddy/debug.log
}
# ... diğer kurallar
}
Performans Gözlemleri
Yönlendirme kuralları yazarken performansı da göz önünde bulundurman lazım. Bazı pratik ipuçları:
- Sık eşleşen kuralları üste koy: Caddy kuralları sırayla değerlendirmez ama matcher spesifikliği önemli. Net path eşleşmeleri regex’ten daha hızlı.
- Gereksiz regex kullanma: Basit path eşleşmesi yetiyorsa
pathkullan,path_regexpyalnızca gerekince. - Redirect zincirlerinden kaç: A -> B -> C yerine direkt A -> C yönlendirmesi yap. Her redirect ekstra round-trip demek.
- 301 ile dikkatli ol: Kalıcı redirect’ler tarayıcı cache’ine girer. Geri almak zor olabilir. Test aşamasında 302 kullan.
Sonuç
Caddy’nin rewrite ve redirect sistemi, Nginx veya Apache’ye kıyasla çok daha okunabilir ve yönetilebilir. Named matcher’lar sayesinde karmaşık koşulları anlamlı isimlerle tanımlayabiliyor, snippet’lerle ortak kuralları paylaşabiliyor, handle blokları ile öncelik sırasını net olarak belirleyebiliyorsun.
Gördüğümüz senaryoları özetlersek: Basit path redirect’lerinden regex tabanlı URL dönüşümlerine, maintenance modundan microservice yönlendirmelerine kadar Caddy tek bir config dosyasında bunların hepsini temiz bir şekilde hallediyor. rewrite ile redir arasındaki farkı iyi anlamak ve doğru matcher’ı seçmek, sağlıklı bir URL yönetim stratejisinin temelini oluşturuyor.
Kendi projende kullanmak için öneririm: Önce basit kurallarla başla, test et, sonra karmaşıklaştır. Redirect zincirlerini düzenli olarak kontrol et, gereksiz yönlendirmeleri temizle. Ve tabii ki production’a geçmeden önce caddy validate ile syntax kontrolünü ihmal etme.