Resim ve medya dosyalarını S3 uyumlu bir nesne depolama alanından (hetzner Object Storage, AWS S3 veya DigitalOcean Spaces gibi) servis ediyorsanız, muhtemelen URL’leri şu şekilde görüyorsunuz:
https://storage.example.com/my-bucket/project/files/posts/hello-world/cover.png
Bu URL’ler kötü görünüyor. Depolama sağlayıcınızı ve kova adınızı ifşa ediyor. Eğer kova gizli ise, bu URL’leri doğrudan servis edemezsiniz; imzalı URL’lere veya bir proxy’ye ihtiyacınız olur.
Laravel blogumda ham S3 medya URL’lerini, uygulama üzerinden temiz proxy’lenmiş yollar ile nasıl değiştirdiğimi ve böylece performans için 24 saatlik CDN önbelleklemesini nasıl koruduğumu burada bulabilirsiniz.
Mimari
Mimari
Traefik ile Cloudflare arkasında tipik bir Laravel + Octane yığını çalıştırıyoruz. Tüm medya, iki kova içerisinde S3 uyumlu bir nesne depolama alanında saklanıyor: yüklenen medya için gizli bir (project/files) ve otomatik olarak üretilen Open Graph resimleri için kamuya açık bir (project/og-images) kova.
Başlangıçta uygulama, Storage::url() kullanarak nesne depolama alanına doğrudan URL’ler üretiyordu. Bu da:
- Gizli kova URL’lerinin erişilemez olması (kamuya açık hale geçmek seçenek değildi)
- Her sayfanın HTML’inde sağlayıcı bilgileri ifşa olması
- Uygulama düzeyinde önbellekleme kontrolü olmaması
Adım 1: Bir URL yardımcı programı oluşturun
Adım 1: Bir URL yardımcı programı oluşturun
Öncelikle, iki disk için URL üretimini merkezi hale getiren bir MediaUrlBusiness sınıfı oluşturdum. Artık blade şablonlarında Storage::disk('og-images')->url($path) veya Storage::url($path) çağırmak yerine şu şekilde çağırıyorum:
MediaUrlBusiness::forOgImage($path); // → /storage/og-images/posts/slug.png
MediaUrlBusiness::forMedia($path); // → /storage/media/posts/slug/cover.png
Bu, temiz bir göreceli yol döndürüyor; artık daha önce ham S3 URL’leri üreten 6+ blade şablonunun her biri bu tek yardımcıyı kullanıyor.
Adım 2: Proxy denetleyicisi
Adım 2: Proxy denetleyicisi
ObjectProxyController, tüm /storage/{disk}/{path} isteklerini yakalar. Bu:
- Diski izin verilenlerden biri olduğuna dair doğrular (
media→ gizli kova,og-images→ kamuya açık kova) - Kovadaki yapılandırılmış ön ek kullanarak tam S3 anahtarını çözümler
- Doğru
Content-TypeveContent-Lengthbaşlıkları ile nesne depolama alanından dosyayıresponse()->stream()ile akıtır - Cloudflare ve tarayıcıların agresif şekilde önbelleklemesini sağlamak için
Cache-Control: public, max-age=86400başlığını ayarlar
İşte bunun özü:
$disk = Storage::disk($diskMap[$diskSegment]);
$stream = $disk->readStream($fullPath);
return response()->stream(
callback: function () use ($stream) {
fpassthru($stream);
fclose($stream);
},
headers: [
'Content-Type' => $disk->mimeType($fullPath),
'Content-Length' => $disk->fileSize($fullPath),
'Cache-Control' => 'public, max-age=86400, immutable',
]
);
Bir ayrıntı beni yanıltmıştı: Storage::download() ve streamDownload() dosyanın tamamını belleğe alıyor. Büyük resimler için bu bellek israfına yol açıyor. readStream() + response()->stream() kullanarak dosyayı S3’ten doğrudan istemciye gönderiyorum.
Not: Laravel’in S3 sürücüsü fileSize() yöntemini destekliyor. Özelleştirilmiş diskler için $disk->size() kullanın.
Önemli: Gizli kovadaki herhangi bir şey şimdi, biri yolu tahmin ederse proxy aracılığıyla erişilebilir. Bu kovada yalnızca kamuya açık varlıkların bulunmasını sağlayın.
Adım 3: Route ve Middleware ayarları
Adım 3: Route ve Middleware ayarları
Proxy rotası, diğer önbelleklenebilir ön yüz rotaları ile beraber routes/static.php‘da bulunmaktadır:
Route::get(uri: '/storage/{disk}/{path}', action: ObjectProxyController::class)
->where('path', '.*')
->name('storage.proxy');
SetCacheControlHeader middleware’ını, zaten Cache-Control başlığı ayarlanmış yanıtları atlamak için de değiştirdim (çünkü proxy denetleyicisi kendi başlığını ayarlıyor). Böylece middleware, özenle ayarladığımız 24 saatlik önbellek politikasını geçersiz kılmaz.
Adım 4: Her yerde bağlayın
Adım 4: Her yerde bağlayın
MediaUrlBusiness yardımcı programı artık:
- Post, page ve tag gösterim görünümlerinde, hem OG resimleri hem de öne çıkan resimler için
- Anasayfa ve arşiv listelerinde, post kart resimleri için
- Vaka çalışması bileşenlerinde, hizmet resimleri için
OgImageBusiness, üretilen OG resimlerini saklamak ve almak için
Her bir Storage::disk('og-images')->url(...) veya Storage::url(...) çağrısı, uygun MediaUrlBusiness::for*() yöntemleri ile değiştirildi.
Admin paneli ne olacak?
Admin paneli ne olacak?
Filament admin panelinin kendi medya proxy’si var. Mevcut MediaProxyController, editörlerin RichEditor’da resimleri tarayıp eklerken CORS sorunlarını ele alıyor. Bu olduğu gibi kalacak; yetkilendirilmiş ve halka açık önbellekleme için tasarlanmamış.
Sonuç
Sonuç
Önce:
src="https://storage.example.com/my-bucket/project/files/posts/hello-world/cover.png">
Sonra:
src="/storage/media/posts/hello-world/cover.png">
Temiz, sağlayıcıdan bağımsız ve önbellek dostu. URL’lerim artık depolama sağlayıcımı veya kova adımı sızdırmıyor. Eğer bir gün sağlayıcı değiştirirsem, tek yapmam gereken disk yapılandırmasını değiştirmek. Kamuya açık URL’ler aynı kalıyor.
Kaynak: Orijinal Makale



