Bir URL kuralı, istemciye yeni adresi bildiren dış bir 3xx yönlendirme yanıtı üretebilir veya sunucunun isteği başka bir dahili hedefle eşleştirmesini sağlayabilir. İlk yöntemde tarayıcı yeni URL'ye ayrı bir istek gönderir; dahili rewrite işleminde ise kullanıcı çoğu durumda adres çubuğunda ilk URL'yi görmeye devam eder.

Apache ve Nginx aynı sonucu farklı yönergeler ve farklı işleme sıralarıyla üretir. Bu nedenle bir Apache kuralını Nginx'e, bir Nginx kuralını da Apache'ye doğrudan kopyalamayın; önce amacı, yapılandırma bağlamını, sorgu dizesini ve hedef durum kodunu belirleyin.

Dış Yönlendirme ile Dahili Yeniden Yazma Arasındaki Farklar

Dış yönlendirme, sunucunun 301, 302, 307 veya 308 gibi bir yanıtla yeni hedefi Location başlığında bildirmesidir. İstemci bu hedefe web istek ve yanıtlarının kurallarını belirleyen HTTP üzerinden yeni bir istek gönderir. Bu nedenle ek bir istek-yanıt turu oluşur ve görünen URL değişir. Dahili yeniden yazma ise kaynağı tanımlayan URI'yi (Uniform Resource Identifier) sunucu içinde başka bir kaynak, dosya veya işleyiciyle eşleştirir; istemciye yeni adres gönderilmez. HTTPS, HTTP iletişiminin şifreli bağlantı üzerinden yapılmasını sağlar.

İki davranış aşağıdaki özelliklerle karşılaştırılabilir.

Karşılaştırılan ÖzellikDış 3xx yönlendirmesiDahili yeniden yazma
Tarayıcıdaki URLHedef adresle değişirGenellikle değişmez
İstek sayısıİstemci yeni istek gönderirİşlem sunucu içinde sürer
Tipik kullanımHTTPS, alan adı tercihi, kalıcı veya geçici taşımaTemiz URL'yi uygulama girişine veya dosyaya eşleme
Olası hataZincir, döngü, yanlış durum koduYanlış iç hedef, tekrar işleme, beklenmeyen location bloğu seçimi

Sunucuya veri göndermek için kullanılan POST isteklerinde durum kodu ayrıca önem taşır. 301 ve 302 sonrasında istemci POST yöntemini kaynağın içeriğini isteyen GET yöntemine çevirebilir; 307 ve 308 ise yöntemi ve istek gövdesini koruyan yönlendirmeler olarak tanımlanır. Bu nedenle form gönderimi veya API isteği gibi durumlarda yalnız URL'nin değil, yöntemin de test edilmesi gerekir.

Apache 2.4'te Yönlendirme ve Yeniden Yazma Kuralları

Örnekler Apache HTTP Server 2.4 bağlamındadır. mod_rewrite kuralları için modülün etkin olması gerekir. .htaccess içindeki yönergelere, ilgili dizinin ana yapılandırmasında AllowOverride sınıflarıyla veya AllowOverrideList üzerinden tek tek izin verilmelidir; bütün FileInfo sınıfını açmak tek seçenek değildir. Ana sunucu yapılandırmasına erişim varsa kuralları ilgili sanal hostta toplamak, hangi alan adı ve port için çalıştıklarını daha açık gösterir.

VirtualHost (sanal host) ile .htaccess Arasında Seçim

Sanal host içindeki kural alan adı, port ve TLS bağlamıyla birlikte görülür; bu nedenle HTTP ile HTTPS sanal hostlarını ayırmak ve her alan adı varyantına farklı davranış vermek daha kolaydır. .htaccess ise yalnız yerleştirildiği dizin ve altındaki isteklerde çalışır, ana yapılandırmadaki AllowOverride ve AllowOverrideList izinlerine tabidir ve sanal hosta ait tüm kararları tek başına göstermez.

Kuralların sırası her iki bağlamda da önemlidir. Aynı ana sunucu veya VirtualHost bağlamında mod_rewrite, mod_alias yönergelerinden önce çalışır; .htaccess gibi dizin bağlamlarında ise mod_alias önce değerlendirilir. Bu nedenle Redirect ile RewriteRule yönergelerini aynı kapsamda karıştırmayın. Basit taşımalarda mod_alias, koşullu eşleştirmelerde mod_rewrite seçin ve aynı modüldeki kuralları özelden genele sıralayın. Daha özel taşıma kuralı genel HTTPS veya alan adı kuralından sonra kalırsa önce genel kural çalışabilir ve hedef yol beklenenden farklı oluşabilir. Yayın öncesinde her kuralın kaynak desenini, hedefini, dış veya dahili işlem türünü ve durdurma bayrağını ayrı satırda belgeleyin.

Basit Taşımalarda mod_alias Kullanımı

Koşul veya karmaşık düzenli ifade gerektirmeyen dış yönlendirmelerde Redirect ve RedirectMatch yönergeleri yeterlidir. İlk örnek bir yol ağacını yeni bölüme taşır ve eşleşen kısmın devamını hedefe ekler; ikinci örnek yalnız belirtilen tek sayfayı eşleştirir.

Redirect permanent "/eski-bolum/" "https://example.com/yeni-bolum/"
RedirectMatch 301 "^/eski-sayfa/?$" "https://example.com/yeni-sayfa"

Redirect, yol ön ekiyle çalıştığı için kapsamı gereğinden geniş seçmemek gerekir. Tek URL hedefleniyorsa başlangıç ve bitiş sınırları olan RedirectMatch veya açık bir RewriteRule kuralın kapsamını yalnız hedef URL ile sınırlar.

HTTP ve www Varyantlarını Tek Hedefte Birleştirme

HTTP'den HTTPS'ye geçiş ile www tercihi ayrı kurallarda art arda uygulanırsa iki yönlendirmelik zincir oluşabilir. Port 80 üzerinden karşılanan alan adı varyantlarını doğrudan tercih edilen canonical HTTPS adresine göndermek ve HTTPS üzerindeki www sanal hostunu aynı hedefe yönlendirmek, her başlangıç adresinin son hedefe tek yönlendirmeyle ulaşmasını sağlar.

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    Redirect permanent "/" "https://example.com/"
</VirtualHost>

<VirtualHost *:443>
    ServerName www.example.com
    SSLEngine on
    # www adı için geçerli sertifika yönergeleri burada bulunur.
    Redirect permanent "/" "https://example.com/"
</VirtualHost>

Bu bloklar tek başına eksiksiz bir sunucu yapılandırması değildir: example.com örnek alan adı yerine kendi alan adınız yazılmalı, 80 ve 443 portları için Listen yönergeleri, TLS modülü ve gerçek sertifika ile özel anahtar yolları ayrıca tanımlanmalıdır. example.com alan adının içeriğini sunan HTTPS sanal hostu da ayrı bulunmalıdır. TLS bağlantısı yönlendirme yanıtından önce kurulduğu için www sanal hostundaki sertifika www.example.com adını da kapsamalıdır; aksi halde tarayıcı yönlendirmeyi almadan sertifika uyarısı gösterebilir.

.htaccess Deseninde Baştaki Eğik Çizgi

Belge kökündeki .htaccess dosyasında RewriteRule deseni baştaki eğik çizgi olmadan yazılır. Sanal host bağlamında ise RewriteRule deseni, başında eğik çizgi bulunan URL yolu ile karşılaştırılır. Bu nedenle internette bulunan bir kuralı yapılandırma bağlamını doğrulamadan kopyalamak hataya yol açabilir.

RewriteEngine On
RewriteRule ^eski-sayfa/?$ /yeni-sayfa [R=301,END]
RewriteRule ^urun/([0-9]+)$ index.php?urun=$1 [END]

İlk kural istemciye dış yönlendirme döndürür. İkinci kural ise kullanıcıya görünen yolu değiştirmeden isteği dahili olarak index.php hedefiyle işler. Yeni sorgu dizesi eski sorgunun yerini alır: /urun/42?urun=13 isteği yalnız urun=42 parametresiyle işlenir. Örnekte özgün sorgudaki diğer bütün parametreler de bilerek atılır; korunacak parametreler gerekiyorsa bunları uygulamanın kabul ettiği ad ve değerlere göre ayrıca seçin. Bu eşleştirme, uygulamadaki erişim denetiminin yerine geçmez.

Nginx'te return, rewrite ve location Bloğu Davranışı

Nginx, isteği karşılayan sunucu bloğunun rewrite modülü yönergelerini location seçiminden önce işler. Bu aşamada yanıt döndürülmezse URI'ye uyan location bloğunu seçer ve o bloğun yönergelerine geçer. location, istek yoluna göre hangi yapılandırmanın uygulanacağını belirleyen bloktur. Basit ve sabit dış yönlendirmelerde return doğrudan yanıt üretir. rewrite ise düzenli ifadeyle URI'yi değiştirebilir, dış yönlendirme döndürebilir veya değişen URI için yeni bir location bloğu araması başlatabilir.

location seçimi yalnız fiziksel dosya yolunu belirlemez; dahili yeniden yazma sonrasında hangi işleyicinin çalışacağını da etkiler. İç içe olmayan, yalnız yol ön eki ve düzenli ifade kullanan bloklarda tam eşleşmeli location = bulunduğunda arama biter. Aksi halde en uzun eşleşen ön ek aday olarak tutulur; düzenli ifadeler tanım sırasıyla sınanır ve ilk eşleşme kullanılır. Düzenli ifade eşleşmezse aday ön ek seçilir. En uzun eşleşen ön ek ^~ taşıyorsa düzenli ifade araması atlanır. Bu sıra, geniş kapsamlı düzenli ifadeli location'ın beklenmedik biçimde özel bir yolu ele geçirmesini veya dahili yeniden yazma sonrasında farklı işleyicinin devreye girmesini açıklayabilir.

HTTPS ve Alan Adı Tercihinde return Kullanımı

Aşağıdaki server blokları, hem HTTP isteklerini hem de HTTPS üzerindeki www varyantını doğrudan kök alan adının HTTPS adresine gönderir. request_uri değişkeni özgün yolu ve sorgu dizesini birlikte taşıdığı için örneğin /urun?id=7 isteği hedefte de korunur.

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name www.example.com;
    # www adı için geçerli ssl_certificate yönergeleri burada bulunur.
    return 301 https://example.com$request_uri;
}

Örnek alan adı değiştirilmeli, 443 bloğuna gerçek sertifika ve özel anahtar yönergeleri eklenmeli ve içeriği sunan HTTPS server bloğu ayrıca tanımlanmalıdır. Örnek dinleyiciler IPv4 içindir; site IPv6 üzerinden de erişilebilir olacaksa uygun IPv6 listen yönergeleri ayrıca gerekir. Reverse proxy (ters proxy) veya CDN TLS bağlantısını sonlandırıyorsa arka uç, istemcinin HTTPS kullandığını doğrudan göremeyebilir; yönlendirme koşullarını yalnız güvenilen ters proxy tarafından belirlenen başlıklara göre kurun. Bu katman, istemciden gelen aynı adlı başlığı doğrudan aktarmamalı; özgün bağlantının HTTP mi HTTPS mi olduğunu kendisi belirlemelidir.

Tek URL ve Yol Ağacını Taşıma

Tek bir URL için tam eşleşmeli location kullanmak, benzer yolların yanlışlıkla etkilenmesini önler. Hedef URL elle oluşturulduğunda is_args ve args değişkenleri özgün sorgu dizesini koşullu soru işaretiyle birlikte ekler.

location = /eski-sayfa {
    return 301 https://example.com/yeni-sayfa$is_args$args;
}

location ^~ /eski-bolum/ {
    rewrite ^/eski-bolum/(.*)$ https://example.com/yeni-bolum/$1 permanent;
}

İkinci kural aynı sunucudaki yol ağacını taşır ve permanent bayrağı 301 yanıtı üretir. Alan adı da değişecekse rewrite yönergesinin hedef bölümünde sabit ve doğrulanmış tam adres kullanılmalı veya yönlendirme ayrı sunucu bloğunda yapılmalıdır.

Dahili URI Değişimi ve location Bloğu Seçimi

Bu örnekte görünen /urun/42 yolu sunucu içinde /index.php?urun=42 URI'sine çevrilir. last bayrağı mevcut rewrite dizisini durdurur ve değişen URI için yeni bir location bloğu araması başlatır; istemciye 3xx yanıtı göndermez.

location / {
    rewrite ^/urun/([0-9]+)$ /index.php?urun=$1? last;
}

Hedefin sonundaki ikinci soru işareti eski sorgunun eklenmesini engeller; /urun/42?urun=13 isteği yalnız urun=42 parametresine dönüşür. Özgün sorgudaki bütün diğer parametreler de bu örnekte atılır. last sonrasında yeni URI yeniden değiştirilip aynı kurallara dönüyorsa döngü oluşabilir. break ise rewrite yönergelerini durdurur ve yeni location araması başlatmadan işlemeyi mevcut blokta sürdürür.

Bu kural PHP çalıştırıcısını kurmaz. /index.php isteğini PHP çalıştırıcısına ileten ayrı bir FastCGI (Fast Common Gateway Interface) yapılandırması gerekir; FastCGI, web sunucusuyla uygulama işlemi arasındaki iletişimi sağlar. Apache örneğinde de PHP dosyalarını çalıştıran işleyici önceden yapılandırılmalıdır. PHP dosyasının statik dosya olarak sunulmadığı ayrıca doğrulanmalıdır.

Apache ve Nginx'te Sorgu Parametreleri Nasıl Korunur?

Apache mod_rewrite hedefinde yeni sorgu yoksa özgün sorgu korunur; yeni sorgu yazılırsa özgün sorgunun yerini alır. QSA bayrağı bu iki sorguyu birleştirir. QSD ise hedefinde sorgu bulunmayan kurallarda özgün sorguyu atmak için kullanılır; hedefte yeni sorgu varsa zaten değiştirme davranışı uygulanır.

Nginx return hedefinde özgün yol ve sorgu $request_uri ile birlikte taşınabilir. Yol elle değiştiriliyorsa $is_args$args yalnız sorguyu gerekli soru işaretiyle ekler. Nginx rewrite ise yeni sorgu parametrelerinin arkasına özgün sorguyu varsayılan olarak ekler; hedefin sonuna eklenen soru işareti bunu engeller.

İki sorguyu birleştirmek aynı adlı parametreyi çoğaltabilir. Örneğin yoldan çıkarılan urun=42 değerinin yanına istemcinin gönderdiği urun=13 eklenirse uygulama yanlış ürünü seçebilir. Hangi parametrelerin korunacağını belirleyin ve uygulamanın yinelenen parametreleri nasıl işlediğini test edin.

Yönlendirme Hedefi Nasıl Sınırlandırılır?

Yönlendirme hedefinin alan adını doğrulanmamış Host başlığından, sorgu parametresinden veya başka bir kullanıcı girdisinden doğrudan üretmeyin. Sabit ya da izin listesiyle doğrulanmış hedef kullanın; aksi halde saldırganın ziyaretçiyi kendi seçtiği adrese gönderebildiği açık yönlendirme açığı oluşabilir. Düzenli ifade geri başvurularındaki özel ve yüzde kodlu karakterleri de her sunucunun kaçış kurallarına göre test edin; ham girdiyi hedef URL'ye denetimsiz eklemeyin.

Yönlendirme Zinciri, Döngü ve Yanlış Eşleşme Nasıl Bulunur?

Her kaynak varyantı için hedef şemayı, alan adını, portu ve yolu önceden tek bir tercih edilen biçimde belirleyin. www kullanan HTTP adresi, kök alan adının HTTP adresi ve www kullanan HTTPS adresi gibi girişlerin doğrudan aynı son adrese gitmesi, gereksiz yönlendirme zincirini azaltır.

  • Tercih edilen hedefin başka bir kuralla tekrar kaynak adrese gönderilmediğini kontrol edin.
  • Tam URL, alt yol, sondaki eğik çizgi, büyük-küçük harf ve yüzde kodlu karakter örneklerini ayrı test edin.
  • Sorgu dizesinin korunması, birleştirilmesi veya silinmesi kararını her kuralda bilinçli verin.
  • POST ve diğer yöntemlerde 301, 302, 307 ve 308 davranışını gerçek istemci veya test aracıyla doğrulayın.
  • CDN, yük dengeleyici ve ters proxy katmanlarının ek yönlendirme üretip üretmediğini inceleyin.

Location yanıt başlığını tek adımda görmek için yanıt gövdesini istemeden başlıkları alan HEAD yöntemini kullanın; bütün zinciri görmek için yönlendirme sayısını sınırlayan ve yönlendirmeleri izleyen bir istek gönderin. Bazı uygulamalar HEAD ile GET yöntemlerini farklı işleyebildiğinden ilgili rotayı GET ile, form ve API akışlarını da kullandıkları gerçek yöntemle doğrulayın.

curl -I "http://www.example.com/eski-sayfa?ref=1"
curl -I -L --max-redirs 10 "http://www.example.com/eski-sayfa?ref=1"

Yayın Öncesi Test ve Geri Dönüş Planı

Kalıcı yönlendirmeler tarayıcı, ara önbellek veya CDN tarafından saklanabildiği için doğrudan üretimde deneme yapmak geri dönüşü zorlaştırabilir. İlk denemeleri ayrı test ortamında yapın. Geçici 302 veya 307 yanıtı kullanmak kalıcı yönlendirmenin etkisini azaltabilir; ancak bu yanıtlar da uygun önbellek başlıklarıyla saklanabilir. Test yanıtlarının saklanmaması için Cache-Control: no-store kullanılması, daha önce saklanmış yanıtları kendiliğinden silmez. Son kontrolde yayında kullanılacak gerçek durum kodunu da sınayın.

  1. Mevcut kuralları, CDN davranışını ve uygulama içindeki yönlendirmeleri listeleyin.
  2. Apache için apachectl configtest, apache2ctl -t veya httpd -t; Nginx için nginx -t ile söz dizimini doğrulayın.
  3. Kaynak ve hedef URL'leri test ortamında sorgu dizesi, yöntem ve sondaki eğik çizgi varyantlarıyla sınayın.
  4. Değişikliği küçük bir kapsamda yayınlayın; erişim ve hata günlüklerinde 3xx, 4xx ve 5xx artışını izleyin.
  5. Eski yapılandırmayı sürümlü biçimde saklayın. Geri dönüşte ilgili sürümü yükleme, söz dizimini yeniden doğrulama ve gerekiyorsa sunucu yapılandırmasını yeniden yükleyerek etkinleştirme adımlarını önceden belirleyin.