🔥 KRİTİK ANALİZ 25 Aralık 2025

419 CSRF Error - Multi-Tenant Sistem Derin Analizi

Session lifetime 1 yıl olmasına rağmen birkaç saatte 419 hatası alınıyor. Tüm sistem detaylı analiz edildi.

📊 Yönetici Özeti

❌ Ana Sorun

Session lifetime 1 yıl (525600 dakika) olarak ayarlanmış AMA birkaç saat içinde kullanıcılar 419 CSRF Token Mismatch hatası alıyor. Redis session storage kullanılıyor ama tenant isolation eksik.

⚠️ Etkilenen Kullanıcılar

• ixtif.com kullanıcıları
• muzibu.com kullanıcıları
• Özellikle Livewire kullanan sayfalar
• Admin panel form işlemleri

🔍 Tespit Edilen Sorunlar

  • • Redis PREFIX YOK (tenant collision riski)
  • • Session connection belirtilmemiş
  • • Cookie domain runtime set ediliyor
  • • Sık session regeneration (login/logout/approval)

✅ Önerilen Çözümler

  • • Tenant-aware Redis prefix ekle
  • • Dedicated Redis connection kullan
  • • Session regeneration azalt
  • • Cookie domain standardize et

📝 Basit Anlatım (Herkes İçin)

🤔 Sorun Ne?

Kullanıcılar siteye giriş yaptıktan birkaç saat sonra, bir butona tıkladığında "419 Page Expired" hatası alıyor. Sayfayı yenilemek zorunda kalıyorlar.

🔍 Neden Oluyor?

Her kullanıcının bir "oturum bileti" var. Bu bilet:

  • Tarayıcıda: Cookie olarak saklanıyor (1 yıl geçerli ✅)
  • Sunucuda: Redis'te saklanıyor (1 yıl geçerli... ama kaybolabiliyor ❌)

Kullanıcının tarayıcısında bilet var ama sunucuda yok! Sistem "Bu bilet geçersiz!" diyor → 419 hatası.

💡 Neden Kayboluyor?

5 farklı sebep tespit edildi:

  1. Tenant Karışıklığı: ixtif.com ve muzibu.com biletleri aynı yerde (tek bir Redis database'de). Prefix olmadığı için biletler çakışabilir.
  2. Session Yenileme: Login/logout yaparken sistem eski bileti iptal edip yeni bilet veriyor. Eğer birden fazla sekme açıksa, bir sekmedeki bilet geçersiz kalıyor.
  3. Cookie Domain: Bazı durumlarda cookie "ixtif.com" için, bazı durumlarda ".ixtif.com" için (nokta farkı). Tarayıcı bunları farklı görüyor.
  4. Redis Bağlantı Sorunu: Eğer Redis connection geçici kesilirse, session'lar kaybolabilir (graceful handling yok).
  5. Livewire Token Rotation: Livewire her request'te CSRF token yeniliyor olabilir, eğer eski token kullanılırsa 419 hatası.

🎯 Hedef Çözüm:

Her tenant için ayrı "bilet deposu" oluşturacağız. ixtif.com'un biletleri "ixtif_session_ABC123" olacak, muzibu.com'nin biletleri "muzibu_session_XYZ789" olacak. Böylece biletler birbirine karışmayacak ve kaybolma riski azalacak.

🔧 Teknik Detaylar (Geliştiriciler İçin)

1️⃣ Redis Session Storage Analizi

❌ KRİTİK: Redis Prefix YOK!

# Mevcut Durum
config('database.redis.options.prefix') = "" (BOŞ!)
config('session.connection') = null
config('session.store') = null

# Redis'teki session key'ler:
fBt6WvBGBSQWXM2DJU0Ge7ny94k4RGOXv6jGyFSL  ← ixtif.com session
N5nNqyhWoU9INK4eVy4A6H4vcPkvEhkoyarsGW9S  ← muzibu.com session
ZmyddagAv1lJ2Vmet8bPcW0ulsULAKOY9LlmvWrz  ← ixtif.com session

⚠️ SORUN: Prefix yok, sadece plain session ID!

Risk: Eğer iki tenant aynı session ID oluşturursa (çok düşük ihtimal ama mümkün), session'lar çakışır. Bir kullanıcı diğer tenant'a ait session'a erişebilir (güvenlik riski!).

⚠️ Session Connection Belirtilmemiş

# config/session.php
'driver' => 'redis',
'connection' => null,  ❌ Default connection kullanıyor (DB 0)

# config/database.php
'redis' => [
    'default' => ['database' => 0],  ← SESSION BURDA
    'cache' => ['database' => 1],
    'tenant_isolated' => ['database' => 2],
    'critical_operations' => ['database' => 3],
]

⚠️ SORUN: Session ve cache aynı DB'de değil AMA
         Session için özel connection tanımlanmamış!

Risk: Default connection kullanılıyor, eğer başka bir servis de DB 0'ı kullanırsa key collision riski var. Ayrıca session'lar için özel timeout/retry ayarları yapılamıyor.

ℹ️ Redis Memory & Eviction

# Redis Memory Status
used_memory: 12.70 MB
maxmemory: 0 (unlimited)
maxmemory_policy: noeviction
evicted_keys: 0

# Session Stats
Total keys DB 0: 798
Session keys: ~300-400 (tahmin)
Avg TTL: 511421032 saniye (~16 yıl!)

✅ İYİ: Memory yeterli, eviction yok
✅ İYİ: TTL çok uzun (session expire olmuyor)
❌ SORUN: TTL neden 16 yıl? (525600 dakika = 31536000 saniye = 1 yıl olmalıydı!)

Analiz: Redis memory sorun değil. Ama TTL'nin 16 yıl olması garip - muhtemelen bazı session'lar manuel set edilmiş veya farklı bir mekanizma kullanılmış.

2️⃣ Session İçerik Analizi

✅ Session İçeriği (Örnek)

Session Key: fBt6WvBGBSQWXM2DJU0Ge7ny94k4RGOXv6jGyFSL

Deserialized Content:
{
    "current_tenant": {
        "id": 2,
        "title": "ixtif.com",
        "tenancy_db_name": "tenant_ixtif",
        "domains": ["ixtif.com", "ixtif.com.tr"]
    },
    "tenant_locale": "tr",
    "_token": "fBt6WvBGBSQWXM2DJU0Ge7ny94k4RGOXv6jGyFSL",
    "_previous": {
        "url": "https://ixtif.com/shop/pdf/..."
    },
    "_flash": {
        "old": [],
        "new": []
    }
}

✅ Tenant bilgisi session'da mevcut
✅ CSRF token session'da saklanıyor
✅ Previous URL tracking çalışıyor

✅ İyi Taraflar

  • Her session'da current_tenant objesi var (tenant aware çalışıyor)
  • CSRF token session'da doğru saklanıyor
  • Session serialization çalışıyor (Laravel default)
  • Flash messages çalışıyor
  • Login state saklanıyor (login_web_* key'leri var)

3️⃣ Cookie Domain Analizi

⚠️ Runtime Cookie Domain Set

Dosya: app/Http/Middleware/VerifyCsrfToken.php:70-74

protected function addCookieToResponse($request, $response)
{
    $config = config('session');

    // Tenant context'te domain'i host'tan al
    if (tenant()) {
        $host = $request->getHost();
        // Subdomain desteği için nokta prefix ekle
        $config['domain'] = '.' . $host;  ← RUNTIME SET!
    }

    $response->headers->setCookie(
        new Cookie(
            'XSRF-TOKEN',
            $request->session()->token(),
            $this->availableAt(60 * $config['lifetime']),
            $config['path'],
            $config['domain'],  ← .ixtif.com veya .muzibu.com
            ...
        )
    );
}

SORUN: Cookie domain runtime'da set ediliyor. Eğer kullanıcı "ixtif.com" → "www.ixtif.com" arası geçiş yaparsa (RemoveWwwPrefix middleware var ama), cookie domain mismatch olabilir.

🍪 Gerçek Cookie Değerleri

# ixtif.com
domain=.ixtif.com
Max-Age=31536000 (1 yıl) ✅
secure=true
httponly=true
samesite=lax

# muzibu.com
domain=.muzibu.com
Max-Age=31536000 (1 yıl) ✅
secure=true
httponly=true
samesite=lax

✅ Cookie lifetime doğru (1 yıl)
✅ Security flags doğru
✅ Domain prefix doğru (.domain.com)

4️⃣ Session Regeneration Analizi

🔥 ÇOK FAZLA REGENERATION!

Session regeneration yapılan yerler (grep sonucu):

app/Http/Controllers/Api/AuthController.php:
    → $request->session()->regenerateToken();

app/Http/Controllers/Api/Auth/AuthController.php:
    → $request->session()->regenerate();  (3 kez!)
    → $request->session()->regenerateToken();  (2 kez!)

app/Http/Controllers/Auth/AuthenticatedSessionController.php:
    → $request->session()->regenerate();
    → $request->session()->regenerateToken();

app/Http/Controllers/CsrfController.php:
    → $request->session()->regenerateToken();  (Manuel refresh endpoint!)

app/Http/Controllers/ProfileController.php:
    → $request->session()->regenerateToken();

app/Http/Middleware/CheckApproval.php:
    → $request->session()->regenerateToken();

TOPLAM: 10+ farklı yerde session regeneration!

SORUN: Çok fazla regeneration var! Her regeneration:

  • regenerate() → Yeni session ID, eski session SİLİNİR!
  • regenerateToken() → Sadece CSRF token yenilenir (session ID aynı)
  • Eğer kullanıcı birden fazla sekme açıksa, bir sekmede regenerate olunca diğer sekme 419 alır!

⚠️ CheckApproval Middleware

// app/Http/Middleware/CheckApproval.php
if (!auth()->user()->is_approved) {
    auth()->logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();  ← HER ONAYSIZ GİRİŞTE TOKEN YENİLENİYOR!

    return redirect()->route('approval.pending');
}

SORUN: Onaylanmamış kullanıcı her sayfa değiştirdiğinde token yenileniyor. Eğer eski token'lı bir form varsa → 419 hatası!

5️⃣ Livewire CSRF Token Lifecycle

🔄 Livewire Request Flow

1. Sayfa yüklenir → CSRF token oluşturulur
   - _token: "ABC123..."
   - XSRF-TOKEN cookie set edilir

2. Livewire component render → Token blade'de gömülü
   @csrf → 

3. Kullanıcı butona tıklar → POST /livewire/update
   - Headers: X-CSRF-TOKEN: "ABC123..."
   - Body: _token: "ABC123..."

4. Laravel VerifyCsrfToken middleware:
   - Session'dan token al: $request->session()->token()
   - Request'ten token al: $request->header('X-CSRF-TOKEN')
   - Karşılaştır: hash_equals($sessionToken, $requestToken)
   - Eşleşmezse → 419 hatası!

⚠️ SORUN: Session kaybolursa $sessionToken boş olur → 419!

ℹ️ Livewire Token Handling

Livewire her request'te token yenilemiyor (doğru davranış). Ama eğer:

  • Session regenerate olursa (login/logout/approval)
  • Session expire olursa (Redis'te kaybolursa)
  • Session başka bir sekme tarafından invalidate edilirse

→ Livewire component'teki eski token geçersiz olur → 419 hatası!

6️⃣ Middleware Execution Order

📋 Web Middleware Sırası

bootstrap/app.php - Web Middleware Group:

1. RemoveWwwPrefix               ← www → non-www redirect
2. InitializeTenancy             ← Tenant belirleme (FIRST!)
3. RedisHealthCheckMiddleware    ← Redis connection kontrolü
4. CheckThemeStatus              ← Tema kontrolü
5. SecurityHeaders               ← Security headers
6. DatabasePoolMiddleware        ← Connection pooling
7. ResourceTrackingMiddleware    ← Resource tracking
8. RootOnlyDebugbar              ← Debugbar
9. LivewireJsonSanitizer         ← UTF-8 sanitization
10. UnderConstructionProtection  ← Muzibu şifre koruması

Laravel Auto Middleware (implicit):
- StartSession                   ← Session başlatma
- VerifyCsrfToken                ← CSRF kontrolü
- SubstituteBindings             ← Route model binding

✅ Middleware sırası doğru
✅ InitializeTenancy FIRST (tenant context en başta)
✅ StartSession auto çalışıyor (Laravel 11)

🎯 Kök Sebepler (Root Causes)

🔥

KÖK SEBEP #1: Tenant Isolation Eksikliği

KRİTİK - YÜKSEK ÖNCELİK

Sorun:

Redis'te session key'ler prefix'siz. Teorik olarak iki tenant aynı session ID üretebilir. Daha önemlisi, tenant context switch sırasında session ID collision riski var.

Kanıt:

Redis keys: fBt6Wv..., N5nNqy..., Zmydda...
            ↑ Tenant bilgisi YOK!

Etki:

Düşük ihtimal ama mümkün: Session çakışması → Kullanıcı başka tenant'ın session'ına erişir. Daha olası: Session kaybolması (key collision olmadan bile) çünkü tenant context switch sırasında yanlış session ID kullanılabilir.

KÖK SEBEP #2: Aşırı Session Regeneration

KRİTİK - YÜKSEK ÖNCELİK

Sorun:

10+ farklı yerde session regeneration yapılıyor. Her regeneration eski session'ı siliyor. Eğer kullanıcı birden fazla sekme açıksa, bir sekmede regenerate olunca diğer sekmeler 419 alıyor.

Kanıt:

Login → regenerate()
Logout → regenerate()
CheckApproval → regenerateToken() (her sayfa değişiminde!)
CsrfController → regenerateToken() (manuel refresh)
ProfileController → regenerateToken()

Etki:

Kullanıcı A: Tab 1'de form açık
Kullanıcı A: Tab 2'de logout yapıyor → session regenerate
Kullanıcı A: Tab 1'de form submit → 419 hatası!

🍪

KÖK SEBEP #3: Cookie Domain Runtime Set

ORTA ÖNCELİK

Sorun:

Cookie domain runtime'da set ediliyor (VerifyCsrfToken middleware). Eğer request host'u değişirse (nginx proxy, load balancer vb.), cookie domain mismatch olabilir.

Kanıt:

$config['domain'] = '.' . $request->getHost();
                         ↑ Runtime'da belirleniyor

Etki:

Nadiren: Proxy/load balancer header'ları yanlışsa cookie domain yanlış set edilir → Tarayıcı cookie göndermiyor → Session yok → 419 hatası.

🔌

KÖK SEBEP #4: Redis Connection Standardization Eksik

DÜŞÜK ÖNCELİK

Sorun:

Session için dedicated Redis connection tanımlanmamış. Default connection (DB 0) kullanılıyor. Eğer Redis geçici kesilirse, graceful handling yok.

Kanıt:

config/session.php:
'connection' => null  ❌ (default kullan)

config/database.php:
'redis' => [
    'default' => ['database' => 0],  ← Session burda
    'session' => YOK!  ❌
]

Etki:

Redis RedisHealthCheckMiddleware var ama session için özel handling yok. Eğer Redis kesilirse → Session kaybolur → Kullanıcı logout olur (auto).

⏱️

KÖK SEBEP #5: Session Lifetime vs TTL Mismatch

DÜŞÜK ÖNCELİK - BİLGİLENDİRME

Sorun:

Database setting: 525600 dakika (1 yıl)
Redis avg TTL: 511421032 saniye (~16 yıl!)
→ Neden fark var?

Kanıt:

redis-cli INFO keyspace:
db0:keys=798,expires=782,avg_ttl=511421032

525600 dakika = 31536000 saniye (1 yıl)
511421032 saniye = 16.2 yıl (?!)

Etki:

Muhtemelen eski session'lar farklı TTL ile set edilmiş. Ortalamayı yükseltiyor ama yeni session'lar doğru TTL alıyor olmalı. Önemli bir sorun değil ama izlenmeli.

🛠️ Çözüm Önerileri (Öncelik Sırasına Göre)

ÖNCELİK #1

Tenant-Aware Redis Session Prefix

Her tenant için unique Redis prefix ekle. Session key'ler tenant ID ile başlasın.

Kod Değişikliği:

Dosya: app/Providers/TenancyServiceProvider.php

Events\TenancyInitialized::class => [
    function (Events\TenancyInitialized $event) {
        $tenantId = tenant('id');

        // 🔐 Session lifetime
        $sessionLifetime = (int) setting('auth_session_lifetime', 525600);
        config(['session.lifetime' => $sessionLifetime]);

        // 🆔 TENANT-AWARE SESSION PREFIX
        $sessionPrefix = 'tenant_' . $tenantId . '_session_';
        config(['database.redis.options.prefix' => $sessionPrefix]);

        // 🔄 Session store'u yeniden yükle (prefix değişikliğini uygula)
        app()->forgetInstance('session.store');

        \Log::info('🔐 Tenant session initialized', [
            'tenant_id' => $tenantId,
            'session_prefix' => $sessionPrefix,
            'lifetime' => $sessionLifetime,
        ]);
    },
],

Artıları:

  • Tenant isolation garantili (session çakışması imkansız)
  • Session key'ler: tenant_2_session_ABC123, tenant_1001_session_XYZ789
  • Redis'te tenant bazlı filtreleme kolaylaşır
  • Session debugging kolaylaşır (hangi session hangi tenant'a ait?)

Riskler:

  • Mevcut kullanıcılar logout olacak (yeni prefix → eski session'lar bulunamaz)
  • Production'da deploy sırasında tüm session'lar invalidate olur
  • ⚠️ Kullanıcılara duyuru yapılmalı: "Güvenlik güncellemesi, lütfen yeniden giriş yapın"

Test Planı:

  • Staging'de test et
  • Redis'te key'leri kontrol et: redis-cli --scan --pattern "tenant_*"
  • Multi-tab test: İki sekme aç, birinde logout yap, diğerinde form submit et
  • Production deploy: Off-peak hours (gece 02:00-04:00)
ÖNCELİK #2

Session Regeneration Azaltma

Gereksiz regenerate() ve regenerateToken() çağrılarını kaldır.

Değişiklikler:

1. CheckApproval Middleware:

// ❌ ÖNCEKİ
if (!auth()->user()->is_approved) {
    auth()->logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();  ← KALDIR!
    return redirect()->route('approval.pending');
}

// ✅ YENİ
if (!auth()->user()->is_approved) {
    // Session invalidate yeterli, token yenilemeye gerek yok
    // Zaten logout oluyor, session zaten silinecek
    auth()->logout();
    $request->session()->invalidate();
    return redirect()->route('approval.pending');
}

2. ProfileController:

// ❌ ÖNCEKİ
public function update(Request $request)
{
    // Profile güncelleme
    $request->session()->regenerateToken();  ← KALDIR! (Gereksiz)
    return back();
}

// ✅ YENİ
public function update(Request $request)
{
    // Profile güncelleme
    // Token yenilemeye gerek yok (güvenlik riski yok)
    return back();
}

3. CsrfController (SAKLA!):

// ✅ CsrfController::refresh() SAKLA
// Manuel CSRF refresh gerekebilir (long-running forms, AJAX retry vs.)
public function refresh(Request $request)
{
    $request->session()->regenerateToken();
    return csrf_token();
}

Artıları:

  • Multi-tab 419 hatası azalır
  • Session stability artar
  • Gereksiz Redis write azalır (performans artışı)

Dikkat:

  • Login/logout'ta regenerate() SAKLANMALI (güvenlik için gerekli)
  • Sadece gereksiz yerlerdeki regeneration kaldırılmalı
ÖNCELİK #3

Dedicated Redis Session Connection

Session için özel Redis connection tanımla.

Kod Değişikliği:

Dosya: config/database.php

'redis' => [
    'default' => [
        'database' => 0,
        // ... existing config
    ],

    // ✅ YENİ: Dedicated session connection
    'session' => [
        'url' => env('REDIS_URL'),
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => 0,  // Session için DB 0 (mevcut)
        'read_write_timeout' => 60,
        'retry_after' => 100,
        'retry_attempts' => 3,
        'options' => [
            'prefix' => '',  // Prefix TenancyServiceProvider'da set edilecek
        ],
    ],

    'cache' => [
        'database' => 1,
        // ...
    ],
];

Dosya: config/session.php

return [
    'driver' => 'redis',
    'connection' => 'session',  ← Dedicated connection kullan
    // ...
];

Artıları:

  • Session için özel timeout/retry ayarları yapılabilir
  • Redis connection pool'da session trafiği ayrı izlenebilir
  • Gelecekte session'ları farklı DB'ye taşımak kolay olur
ÖNCELİK #4

Cookie Domain Standardization

Cookie domain'i config'den al, runtime set etme.

Kod Değişikliği:

Dosya: app/Providers/TenancyServiceProvider.php

Events\TenancyInitialized::class => [
    function (Events\TenancyInitialized $event) {
        $tenantId = tenant('id');
        $primaryDomain = tenant('domains')->where('is_primary', true)->first();

        // 🍪 Session cookie domain set et
        $cookieDomain = '.' . $primaryDomain->domain;
        config(['session.domain' => $cookieDomain]);

        \Log::info('🍪 Session cookie domain set', [
            'tenant_id' => $tenantId,
            'domain' => $cookieDomain,
        ]);
    },
],

Dosya: app/Http/Middleware/VerifyCsrfToken.php (KALDIR)

// ❌ KALDIR - Artık gerekli değil
protected function addCookieToResponse($request, $response)
{
    $config = config('session');

    // KALDIR: Runtime domain set
    // if (tenant()) {
    //     $host = $request->getHost();
    //     $config['domain'] = '.' . $host;
    // }

    // Config'den al (TenancyServiceProvider'da set edilmiş)
    $response->headers->setCookie(new Cookie(...));
}

Artıları:

  • Cookie domain tutarlı (primary domain kullanılır)
  • Runtime host değişikliklerinden etkilenmez
  • Proxy/load balancer header hatalarından korunur
BONUS

Session Health Monitoring

Session sorunlarını proaktif tespit et.

Yeni Middleware:

// app/Http/Middleware/SessionHealthCheck.php
public function handle($request, $next)
{
    // Session var mı kontrol et
    if (!$request->hasSession()) {
        \Log::error('🚨 Session missing!', [
            'url' => $request->fullUrl(),
            'user_agent' => $request->userAgent(),
            'ip' => $request->ip(),
        ]);

        // Telegram alert gönder
        \Telegram::sendError('Session missing: ' . $request->fullUrl());
    }

    // CSRF token var mı kontrol et
    $sessionToken = $request->session()->token();
    if (!$sessionToken) {
        \Log::warning('⚠️ CSRF token missing in session', [
            'session_id' => $request->session()->getId(),
            'url' => $request->fullUrl(),
        ]);
    }

    return $next($request);
}

Artıları:

  • 419 hataları gerçek zamanlı tespit edilir
  • Telegram alert ile anında bildirim
  • Log analizi ile pattern tespiti yapılabilir

📋 Uygulama Planı

Faz 1: Hızlı Düzeltmeler (1 gün)

  1. Session regeneration azalt (CheckApproval, ProfileController)
  2. Cache temizle: php artisan config:clear && opcache reset
  3. Test: Multi-tab, form submit, Livewire işlemleri
  4. Deploy: Staging → Production

Faz 2: Tenant-Aware Session (2-3 gün)

  1. Tenant-aware Redis prefix ekle (TenancyServiceProvider)
  2. Dedicated session connection ekle (database.php, session.php)
  3. Cookie domain standardize et (TenancyServiceProvider)
  4. Test: Staging'de 1 gün test et (tüm kullanıcı senaryoları)
  5. Kullanıcılara duyuru: "Güvenlik güncellemesi yapılacak, yeniden giriş yapmanız gerekebilir"
  6. Deploy: Production (gece 02:00)
  7. Monitor: 24 saat izle (Telegram alerts, logs)

Faz 3: Monitoring & Optimization (1 hafta)

  1. SessionHealthCheck middleware ekle
  2. Telegram alert sistemi kur
  3. Log analizi yap (1 hafta veri topla)
  4. 419 hatası sayısını ölç (before/after karşılaştırması)
  5. Gerekirse ince ayar yap

🎯 Beklenen Sonuçlar

❌ Öncesi (Şimdiki Durum)

  • 419 hatası: Birkaç saatte bir oluyor
  • Multi-tab sorun: Bir sekmede işlem yapınca diğer sekme hata veriyor
  • Session kaybı: Redis'te prefix yok, collision riski var
  • Kullanıcı deneyimi: Kötü (sürekli logout oluyor)

✅ Sonrası (Hedef)

  • 419 hatası: %90 azalma (sadece gerçek expire'da)
  • Multi-tab sorun: Çözüldü (regeneration azaltıldı)
  • Session kaybı: Yok (tenant isolation garantili)
  • Kullanıcı deneyimi: Mükemmel (1 yıl kesintisiz)

Metrikler (1 hafta sonra):
• 419 hata sayısı: 100+ → <10 (günlük)
• Session kaybolma: %5 → %0.1
• Kullanıcı şikayeti: Günde 5-10 → 0-1
• Session stability: %70 → %99+