Bir mağazada sepet ve ödeme sayfasını hızlandırma işi, sitenin geri kalanını hızlandırmaktan tamamen ayrı bir problem sınıfıdır. Kategori ve ürün sayfalarını önbelleğe alıp bir gecede uçurabilirsin; sepette bu numara işe yaramaz, çünkü o sayfa tanım gereği kişiye özeldir ve her istek gerçekten sunucuya kadar gitmek zorundadır. Yani mağazanın en hızlı olması gereken adımı, mimari olarak en yavaş olmaya mahkûm adımdır.
İşin can sıkıcı tarafı, bu adımın parasal karşılığının en yüksek olmasıdır. Kategori sayfasında kaybettiğin ziyaretçi zaten satın alma niyeti belirsiz biriydi; ödeme formunda kaybettiğin ziyaretçi ise kartını çıkarmış bir müşteriydi. Bu yazıda checkout hunisindeki gecikmenin gerçekte nereden geldiğini nasıl ölçeceğini, önbellek dışı kalan istekleri nasıl azaltacağını, oturum depolamasını nasıl hızlandıracağını ve ödeme sağlayıcısı ile kargo servislerinin sayfayı nasıl kilitlediğini anlatacağım.
Sepet ve Ödeme Neden Diğer Sayfalardan Farklı#
Farkı yaratan tek şey durumdur (state). Bir kategori sayfası saf bir fonksiyondur: aynı URL herkese aynı çıktıyı verir, o yüzden bir kez üretip milyon kez servis edebilirsin. Sepet sayfası ise ziyaretçinin çerezine, oturumuna, seçtiği kargo yöntemine, uyguladığı kupona ve stok durumuna bağlıdır. Bunların hepsi her istekte yeniden hesaplanır. Tam sayfa önbelleğinin devre dışı kaldığı yer burasıdır ve bu bir eksiklik değil, bir zorunluluktur — aksi halde biri başkasının sepetini görür.
İkinci fark, istek sayısındadır. Modern mağaza yazılımlarında sepet "canlı" davranır: adet değiştirirsin, ara toplam güncellenir; il seçersin, kargo ücreti yeniden hesaplanır; kupon girersin, indirim uygulanır. Bunların her biri arka planda ayrı bir istektir ve her biri tam PHP yığınını yeniden ayağa kaldırır. Sekiz kalemlik bir sepette adet değişimi başına yarım saniye harcayan bir mağaza, kullanıcıya "kilitlendi" hissi verir. Bu yüzden checkout optimizasyonunun özü sayfa boyutu küçültmek değil, sunucuya giden istek sayısını ve her isteğin maliyetini düşürmektir.
Genel mağaza performansının bütününe ilk kez bakıyorsan, önce e-ticaret sitesi hız optimizasyonu yazısındaki sayfa türü ayrımını oturtman bu yazıyı çok daha verimli kılar.
Ölçüm: Checkout Hunisinde Zaman Nereye Gidiyor#
Ölçmeden başlama. Checkout'ta üç ayrı gecikme kaynağı var ve üçü de birbirine benzemez: sayfanın kendi yüklenmesi, kullanıcı etkileşimi sonrası arka plan istekleri, ve ödeme adımında dış servise yapılan çağrılar. Bunları ayrı ayrı ölçmen gerekir.
İlk ölçüm, sepet sayfasının kendi ilk bayt süresidir. Oturum çerezi taşıyan gerçek bir istek atman gerekir, yoksa yanlışlıkla önbellekten servis edilen bir sayfayı ölçersin:
# Gerçek bir oturum çerezi ile sepet sayfasının zaman kırılımı
curl -s -o /dev/null -b "oturum_id=abc123; sepet_dolu=1" \
-w "tls:%{time_appconnect} ttfb:%{time_starttransfer} toplam:%{time_total}\n" \
https://firmaniz.com/sepet
# Örnek çıktı:
# tls:0.118 ttfb:1.240 toplam:1.302
İkinci ölçüm, arka plan isteklerinin süresidir. Tarayıcının ağ sekmesini aç, sepette bir ürünün adedini değiştir ve tetiklenen isteğin süresine bak. Burada 300 milisaniyenin üstündeki her değer kullanıcı tarafından fark edilir. Üçüncü ölçüm ise ödeme adımında dış servise giden çağrılardır; bunlar genellikle sunucu günlüklerinde ya da uygulamanın kendi izleme kayıtlarında görünür.
| Aşama | Hedef | Uyarı eşiği | Tipik suçlu |
|---|---|---|---|
| Sepet sayfası TTFB | 300 ms altı | 700 ms üstü | Oturum kilidi, ağır sorgu |
| Adet güncelleme isteği | 200 ms altı | 500 ms üstü | Tam sepet yeniden hesabı |
| Kargo ücreti sorgusu | 400 ms altı | 1,5 sn üstü | Senkron dış API çağrısı |
| Ödeme başlatma | 800 ms altı | 3 sn üstü | Sağlayıcı gecikmesi |
| Sipariş kaydetme | 1 sn altı | 3 sn üstü | E-posta gönderimi, stok kilidi |
Bu tablodaki son satır özellikle önemlidir. Çok sayıda mağazada "Siparişi Tamamla" düğmesine basıldıktan sonraki bekleme, siparişin veritabanına yazılmasından değil, aynı istek içinde senkron olarak gönderilen onay e-postasından kaynaklanır. Kullanıcı, SMTP sunucusunun yanıt vermesini bekliyordur.
Önbellek Dışı Kalan İstekleri Azaltma#
Sepet sayfasını tamamen önbellekleyemezsin ama sayfanın büyük kısmını önbellekleyebilirsin. Yöntem şudur: sayfayı statik iskelet olarak servis et, yalnızca kişiye özel parçaları (sepet özeti, oturum durumu) ayrı ve küçük bir istekle sonradan doldur. Buna parça bazlı önbellekleme (fragment caching) denir ve doğru kurulduğunda başlıktaki sepet sayacı gibi minik bir bilgi için tüm sayfayı önbellek dışı bırakmaktan kurtarır.
Nginx tarafında ayrım, çerez ve yol bazlı olmalıdır. Kritik nokta, kuralın kapsamını olabildiğince dar tutmaktır; birçok kurulumda tek bir sepet çerezi tüm siteyi önbellek dışına iter:
# Yalnızca gerçekten kişiye özel yolları önbellek dışı bırak
map $request_uri $checkout_yolu {
default 0;
~^/sepet 1;
~^/odeme 1;
~^/hesabim 1;
~^/siparis-takip 1;
}
# Sepet çerezi SADECE dolu sepette yazılmalı; boş sepette silinmeli
map $http_cookie $oturumlu {
default 0;
~*sepet_dolu=1 1;
~*oturum_id= 1;
}
location ~ \.php$ {
set $bypass "${checkout_yolu}${oturumlu}";
fastcgi_cache MAGAZA;
# 00 dışındaki her kombinasyon önbelleği atlar
fastcgi_cache_bypass $checkout_yolu $oturumlu;
fastcgi_no_cache $checkout_yolu $oturumlu;
add_header X-Cache-Status $upstream_cache_status;
}
Buradaki en önemli ayrıntı, ikinci map bloğundaki sepet_dolu=1 koşuludur. Birçok mağaza yazılımı, ziyaretçi siteye girer girmez boş bir sepet çerezi yazar. Sonuç: hiçbir ziyaretçi önbellekten sayfa alamaz, çünkü herkeste sepet çerezi vardır. Önbellek oranın düşükse ilk bakman gereken yer burasıdır. Çerezin yalnızca sepete gerçekten ürün eklendiğinde yazılmasını sağla; birçok platformda bu tek bir ayar ya da küçük bir kanca (hook) ile çözülür.
İkinci kazanç, gereksiz arka plan isteklerini kısmaktır. Adet değişiminde tüm sepeti yeniden hesaplayan bir istek yerine, yalnızca değişen kalemi ve toplamı dönen hafif bir uç nokta kullan. Kullanıcı adet oklarına hızlıca üç kez basarsa üç ayrı istek gitmemeli; girdiyi 300-500 milisaniye geciktirip (debounce) tek istek atmalısın. Bu iki değişiklik, yoğun saatte sunucu yükünü belirgin biçimde düşürür.
Nginx önbellek katmanını hiç kurmadıysan Nginx FastCGI cache yapılandırma yazısı temel kurulumu adım adım anlatıyor.
Oturum Depolamasını Hızlandırma#
Checkout yavaşlığının en az bilinen ama en yaygın sebeplerinden biri oturum depolamasıdır. Varsayılan PHP kurulumunda oturumlar diskte dosya olarak tutulur ve PHP, bir isteğin oturumunu açtığında o dosyayı kilitler. Aynı kullanıcıdan gelen ikinci bir istek, birincisi bitene kadar bekler. Checkout sayfası aynı anda kargo, kupon ve sepet güncellemesi için üç arka plan isteği gönderiyorsa, bunlar paralel çalışmaz; sıraya girerler. Kullanıcının gördüğü 1,5 saniyelik bekleme aslında üç kez 500 milisaniyedir.
Çözüm oturumları belleğe taşımaktır. Redis ya da Memcached bu iş için idealdir; hem disk kilidi ortadan kalkar hem de birden fazla sunucuya yayıldığında oturumlar ortak kalır:
; php.ini - oturumları Redis'e taşı
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?database=1"
session.gc_maxlifetime = 86400
; Kilit süresini sınırla: takılan bir istek diğerlerini süresiz bekletmesin
redis.session.lock_expire = 30
redis.session.lock_wait_time = 20000
Değişiklikten sonra kilitlenmenin gerçekten çözüldüğünü doğrula. En basit test, aynı oturum çerezi ile paralel iki istek atıp toplam sürenin tek isteğin süresine yakın kalıp kalmadığına bakmaktır:
# Aynı oturumla iki paralel istek: seri kalıyorsa kilit sorunu sürüyor demektir
time ( curl -s -o /dev/null -b "PHPSESSID=test123" https://firmaniz.com/sepet & \
curl -s -o /dev/null -b "PHPSESSID=test123" https://firmaniz.com/sepet & \
wait )
Toplam süre tek isteğin iki katıysa kilit hâlâ seri çalışıyordur. Bellek tabanlı oturum ve nesne önbelleği mantığını daha derin anlamak için Memcached nedir yazısına bakabilirsin.
Bir uyarı: oturumları belleğe taşırken kalıcılık ayarını dikkatle seç. Redis'i tamamen bellekte, kalıcılık kapalı çalıştırırsan sunucu yeniden başladığında herkesin sepeti boşalır. Ödeme akışının ortasındaki bir kullanıcı için bu, yavaş sayfadan çok daha kötü bir deneyimdir.
Ödeme Sağlayıcısı ve Kargo Sorgularının Gecikmesi#
Checkout'taki en uzun beklemeler genellikle senin sunucunda değil, dışarıda geçer. Kargo firmasının fiyat API'si, ödeme sağlayıcısının işlem başlatma çağrısı, vergi hesaplama servisi, stok senkronizasyonu... Bunların her biri ağ üzerinden gider ve yavaşladıklarında senin sayfan yavaşlar. Buradaki mühendislik kuralı basittir: bir dış servisi kullanıcıyı bekletecek şekilde senkron çağırıyorsan, o servisin en kötü gününü de kabul etmişsin demektir.
Üç savunma katmanı kur. Birincisi zaman aşımı: her dış çağrının hem bağlantı hem de toplam yanıt için makul bir zaman aşımı olmalı. Zaman aşımı tanımlanmamış bir çağrı, sağlayıcı takıldığında PHP işlemini dakikalarca tutar ve havuzu tüketir.
// Dış servis çağrılarında zaman aşımı ZORUNLUDUR
$ch = curl_init('https://kargo-saglayici.example/api/fiyat');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 2, // bağlantı için en fazla 2 saniye
CURLOPT_TIMEOUT => 4, // toplam yanıt için en fazla 4 saniye
]);
$yanit = curl_exec($ch);
// Zaman aşımında akışı durdurma: makul bir varsayılana düş
if ($yanit === false) {
$yanit = varsayilanKargoUcreti();
}
İkincisi önbellekleme. Kargo fiyatları il bazında saatlerce değişmez; il ve ağırlık kombinasyonunu anahtar yapıp sonucu birkaç saat bellekte tutarsan çağrıların büyük kısmı hiç yapılmaz. Üçüncüsü ise asenkronlaştırmadır: sipariş onay e-postası, muhasebe entegrasyonu, CRM kaydı ve stok bildirimi gibi işler kullanıcıyı bekletmemelidir. Bunları kuyruğa at, arka planda çalışan bir işçi süreç işlesin. "Siparişiniz alındı" ekranı kullanıcıya, veritabanına yazma işlemi biter bitmez gösterilmelidir.
Ön Uç: Checkout Sayfasında Ne Yüklenmeli#
Ödeme sayfası, sitenin en sade sayfası olmalıdır ve pratikte çoğu mağazada en şişkin sayfadır. Sebep şu: tema ve eklentiler script'lerini ayrım yapmadan her sayfaya ekler. Kaydırıcı kütüphanesi, ürün galerisi büyüteci, yorum widget'ı, ısı haritası, öneri motoru — hiçbirinin ödeme formunda işi yoktur ama hepsi oradadır.
Yapman gereken şey, checkout'ta yüklenecekleri beyaz listeye almaktır. Yani "şunları çıkar" değil, "şunlar kalsın" mantığı. Sayfada kalması gerekenler tipik olarak şunlardır:
- Form doğrulama ve maskeleme kodu (kart numarası, telefon, posta kodu).
- Ödeme sağlayıcısının kendi güvenli alan (iframe/SDK) script'i.
- Adres ve kargo seçimini yöneten kendi kodun.
- Dönüşüm ölçümü için tek bir hafif etiket — o da geciktirilmiş olarak.
Bunun dışındaki her şey çıkar. Analitik ve reklam pikselleri konusunda dikkatli ol: dönüşüm ölçümünü kaybetmemek gerekir ama bu etiketlerin sayfanın çizimini bloklaması gerekmez. Sipariş tamamlandı sayfasında etiketi geciktirmeli yükleyip yine de ölçümü almak mümkündür.
Bir de üçüncü taraf yazı tipleri meselesi var. Dış kaynaktan yüklenen bir yazı tipi, ek DNS çözümlemesi ve TLS el sıkışması demektir; ödeme formunda bu gecikme doğrudan hissedilir. Yazı tiplerini kendi alan adından servis et ve yalnızca gerçekten kullandığın ağırlıkları yükle. Tema ve eklentilerin bu tür yükleri nasıl biriktirdiğini tema ve eklenti seçiminin hıza etkisi yazısında ayrıntılı ele aldım.
Sık Yapılan Hatalar#
Boş sepette bile çerez yazmak. En yaygın ve en pahalı hata budur; tüm siteyi önbellek dışına iter ve mağaza sahibi neden önbelleğin işe yaramadığını aylarca anlayamaz. Yanıt başlıklarında X-Cache-Status değerini anonim bir tarayıcıda kontrol et; BYPASS görüyorsan sebebini bul.
Onay e-postasını istek içinde göndermek. SMTP sunucusu yavaşladığında sipariş tamamlama süresi saniyelerce uzar, kullanıcı düğmeye ikinci kez basar ve çift sipariş oluşur. E-postayı kuyruğa al. Bu arada gönderim altyapının kendisi de yavaşlıyorsa SMTP test aracı ile bağlantı ve TLS uyumunu kontrol edebilirsin.
Dış API çağrılarını zaman aşımısız bırakmak. Kargo sağlayıcısının bir dakikalık kesintisi, senin sitenin bir saatlik kesintisine dönüşebilir; çünkü takılan istekler PHP işlem havuzunu doldurur ve sonunda tüm site yanıt vermez hale gelir.
Checkout'u yalnızca boş sepetle test etmek. Geliştirme ortamında iki kalemlik sepetle her şey hızlıdır. Gerçek testi 10-15 kalemli, kuponlu, kargo seçimi yapılmış bir sepetle yap; sorunlar orada ortaya çıkar.
Sipariş tablosunu hiç bakımsız bırakmak. Sipariş, sipariş kalemi ve meta tabloları yıllar içinde büyür; sipariş kaydetme sırasında yapılan indekssiz bir arama bir noktadan sonra saniyeler sürmeye başlar.
Sıkça Sorulan Sorular#
Sepet sayfası neden önbelleğe alınamıyor#
Çünkü içeriği her kullanıcı için farklıdır: sepetteki ürünler, ara toplam, uygulanan kupon ve kargo seçimi kişiye özeldir. Bu sayfayı tam sayfa önbelleğe alırsan bir kullanıcının sepeti diğerine servis edilir ve bu bir performans sorunu değil, veri sızıntısıdır. Doğru yaklaşım sayfayı önbellek dışı tutup üretim maliyetini düşürmek, statik parçaları ise ayrı ayrı önbelleklemektir.
Ödeme sayfam yavaşsa suç ödeme sağlayıcısında mı#
Bazen evet ama varsaymadan ölç. Sunucu günlüklerinde ya da uygulama izlemende sağlayıcıya yapılan çağrının süresini ayrıca kaydet. Çağrı 200 milisaniyede dönüyorsa sorun sende, 3 saniyede dönüyorsa sağlayıcıdadır. İkinci durumda bile yapabileceğin şeyler var: zaman aşımı koymak, çağrıyı kullanıcı formu doldururken arka planda önceden başlatmak ve tekrar denenebilir hale getirmek.
Redis oturum kullanmak zorunda mıyım#
Zorunlu değil ama tek sunuculu bir mağazada bile belirgin fark yaratır, çünkü asıl kazanç dosya kilidinin ortadan kalkmasıdır. Özellikle checkout gibi aynı anda birden fazla arka plan isteği gönderen sayfalarda dosya tabanlı oturum bu istekleri sıraya sokar. Birden fazla sunucuya yayıldığında ise bellek tabanlı oturum artık tercih değil zorunluluktur.
Kargo ücreti hesaplamasını nasıl hızlandırırım#
Üç adım: sonuçları önbelleğe al (il, ilçe ve ağırlık kombinasyonu anahtar olur, birkaç saat TTL yeterlidir), dış çağrıya kısa bir zaman aşımı koy, ve sağlayıcı yanıt vermediğinde akışı kırmak yerine makul bir varsayılan ücrete düş. Kullanıcı en kötü ihtimalle biraz farklı bir tutar görür, ama sipariş akışı durmaz.
Sipariş tamamlandıktan sonraki bekleme nasıl kısalır#
O bekleme genellikle siparişin yazılmasından değil, aynı istek içinde yapılan yan işlerden kaynaklanır: onay e-postası, fatura oluşturma, muhasebe ve CRM entegrasyonları, stok bildirimi. Bunları bir kuyruğa taşı ve arka planda işlet. Kullanıcıya "siparişiniz alındı" ekranını, sipariş veritabanına yazılır yazılmaz göster; kalan işler saniyeler içinde arka planda tamamlanır.
Checkout'ta hangi scriptleri kaldırabilirim#
Beyaz liste mantığıyla ilerle: form doğrulama, ödeme sağlayıcısının kendi SDK'sı, adres ve kargo seçim kodun ve tek bir dönüşüm etiketi kalsın; diğer her şeyi çıkar. Kaldırmadan önce test kopyasında ödeme akışını uçtan uca dene, çünkü bazı temalar kritik olmayan görünen bir script içinde form gönderim kodunu da barındırır.
Kapanış#
Sepet ve ödeme sayfasını hızlandırmanın özü dört alışkanlıkta toplanıyor. Birincisi, sepet çerezini yalnızca sepette gerçekten ürün varken yaz; bu tek değişiklik çoğu mağazada önbellek oranını dramatik biçimde yükseltir. İkincisi, oturumları diskten belleğe taşı ve paralel isteklerin sıraya girmediğini ölçerek doğrula. Üçüncüsü, her dış servis çağrısına zaman aşımı ve makul bir varsayılan koy. Dördüncüsü, kullanıcıyı bekletmesi gerekmeyen her işi kuyruğa al.
Bu iyileştirmelerin bir kısmı uygulama katmanında, bir kısmı ise altyapıda çözülür. Yoğun sepet trafiği olan mağazalar için e-ticaret hosting paketlerimiz NVMe disk ve ayrılmış kaynakla gelir; Redis oturum ve kendi PHP-FPM ayarlarını kurmak istiyorsan VDS sunucu üzerinde tam kontrol sende olur. Sipariş onay e-postalarının gecikmeden ulaşması için ayrı bir gönderim altyapısı düşünüyorsan SMTP sunucu çözümümüz bu yükü siteden ayırır; sunucu tarafını hiç dert etmek istemiyorsan sunucu yönetimi hizmetimiz önbellek, oturum ve izleme kurulumunu üstlenir.