WordPress’te Özel Yükleme Dizini Belirleme
WordPress kurulumlarında medya dosyalarının nereye yükleneceğini kontrol etmek, özellikle büyük ve karmaşık sitelerde ciddi bir ihtiyaç haline gelir. Varsayılan olarak WordPress, tüm yüklemeleri wp-content/uploads/ dizinine ve altındaki yıl/ay klasörlerine kaydeder. Bu yapı küçük bloglar için yeterli olsa da, çok kiracılı yapılar, özel depolama gereksinimleri veya güvenlik politikaları nedeniyle bu dizini değiştirmeniz gerekebilir. İşte tam bu noktada functions.php devreye girer ve WordPress’in yükleme davranışını kökünden değiştirmenizi sağlar.
WordPress Yükleme Sistemi Nasıl Çalışır?
WordPress dosya yüklemelerini yönetirken birkaç temel filtreyi ve fonksiyonu kullanır. Bunların başında wp_upload_dir() fonksiyonu gelir. Bu fonksiyon çağrıldığında, yükleme dizinine dair temel bilgileri döndüren bir dizi oluşturur.
Döndürülen dizideki önemli anahtarlar şunlardır:
- path: Sunucu üzerindeki tam fiziksel yol (örneğin
/var/www/html/wp-content/uploads/2024/01) - url: Tarayıcıdan erişilebilir tam URL
- subdir: Yıl/ay alt dizin yapısı (örneğin
/2024/01) - basedir: Ana yükleme dizininin fiziksel yolu
- baseurl: Ana yükleme dizininin URL’si
- error: Hata varsa hata mesajı, yoksa false
Bu yapıyı anlamak kritik önem taşır, çünkü değiştireceklerimiz tam olarak bu anahtarlardır. WordPress, upload_dir adlı bir filtre kancası sağlar ve bu kancayı kullanarak yukarıdaki tüm değerleri istediğiniz gibi manipüle edebilirsiniz.
Temel Yükleme Dizini Değiştirme
En basit senaryo ile başlayalım: Tüm yüklemeleri farklı bir dizine yönlendirme.
// functions.php
add_filter( 'upload_dir', 'ozel_yukleme_dizini' );
function ozel_yukleme_dizini( $dirs ) {
$dirs['baseurl'] = get_home_url() . '/dosyalar';
$dirs['basedir'] = ABSPATH . 'dosyalar';
$dirs['path'] = $dirs['basedir'] . $dirs['subdir'];
$dirs['url'] = $dirs['baseurl'] . $dirs['subdir'];
return $dirs;
}
Bu kod, yüklemeleri wp-content/uploads/ yerine sitenizin kök dizinindeki dosyalar/ klasörüne yönlendirir. Ancak dikkat etmeniz gereken kritik bir nokta var: Bu dizinin sunucuda var olması ve web sunucusunun bu dizine yazma yetkisine sahip olması gerekir.
Dizini oluşturmak ve izinleri ayarlamak için sunucuda şunu çalıştırın:
mkdir -p /var/www/html/dosyalar
chown www-data:www-data /var/www/html/dosyalar
chmod 755 /var/www/html/dosyalar
Yıl/Ay Klasör Yapısını Devre Dışı Bırakma
WordPress varsayılan olarak yüklemeleri 2024/01/ gibi tarih tabanlı alt dizinlere koyar. Bazı durumlarda bu yapı istenmiyor olabilir. Bunu iki farklı şekilde kaldırabilirsiniz.
Yöntem 1: WordPress yönetim panelinden
Ayarlar > Medya bölümünde “Yüklemeleri yıl ve ay tabanlı klasörlere göre organize et” seçeneğinin işaretini kaldırabilirsiniz.
Yöntem 2: functions.php üzerinden kod ile
// Yıl/ay klasör yapısını devre dışı bırak
add_filter( 'upload_dir', 'yil_ay_yapisini_kaldir' );
function yil_ay_yapisini_kaldir( $dirs ) {
$dirs['subdir'] = '';
$dirs['path'] = $dirs['basedir'];
$dirs['url'] = $dirs['baseurl'];
return $dirs;
}
Bu yöntemin avantajı, temayı veya eklentiyi farklı bir ortama taşıdığınızda ayarın da beraberinde gelmesidir. Özellikle sürüm kontrollü projelerde bu tercih edilir.
Kullanıcıya veya Role Göre Farklı Dizin Belirleme
Gerçek dünyada sıkça karşılaşılan bir senaryo: Farklı kullanıcı rolleri için farklı yükleme dizinleri oluşturmak. Örneğin bir gazetecilik sitesinde muhabirler kendi klasörlerine yüklesin, editörler kendi klasörlerine.
add_filter( 'upload_dir', 'role_bazli_yukleme_dizini' );
function role_bazli_yukleme_dizini( $dirs ) {
if ( ! is_user_logged_in() ) {
return $dirs;
}
$current_user = wp_get_current_user();
$user_roles = $current_user->roles;
if ( in_array( 'editor', $user_roles ) ) {
$klasor = 'editörler';
} elseif ( in_array( 'author', $user_roles ) ) {
$klasor = 'yazarlar/' . $current_user->user_login;
} elseif ( in_array( 'contributor', $user_roles ) ) {
$klasor = 'katkidabulunanlar';
} else {
return $dirs; // Diğer roller için varsayılan
}
$dirs['subdir'] = '/' . $klasor;
$dirs['path'] = $dirs['basedir'] . '/' . $klasor;
$dirs['url'] = $dirs['baseurl'] . '/' . $klasor;
return $dirs;
}
Bu yapıyla editörler yükledikleri dosyaları uploads/editörler/ dizininde, yazarlar ise uploads/yazarlar/kullanici_adi/ dizininde görecek. Dizinlerin otomatik oluşturulması için WordPress’in wp_mkdir_p() fonksiyonuna güvenebilirsiniz, bu fonksiyon gerekli dizinleri recursive olarak oluşturur.
Post Tipine Göre Dizin Yönetimi
WooCommerce kullanan bir site düşünün. Ürün görselleri, blog yazısı görselleri ve sayfa görselleri hepsinin aynı yerde durması yönetimi zorlaştırır. Post tipine göre dizin ayırmak bu sorunu çözer.
add_filter( 'upload_dir', 'post_tipine_gore_dizin' );
function post_tipine_gore_dizin( $dirs ) {
// Hangi post için yükleme yapıldığını bul
$post_id = 0;
if ( isset( $_REQUEST['post_id'] ) ) {
$post_id = absint( $_REQUEST['post_id'] );
} elseif ( isset( $_POST['post_id'] ) ) {
$post_id = absint( $_POST['post_id'] );
}
if ( $post_id > 0 ) {
$post_type = get_post_type( $post_id );
$dizin_haritasi = array(
'product' => 'urunler',
'post' => 'blog',
'page' => 'sayfalar',
'portfolio' => 'portfolyo',
);
if ( isset( $dizin_haritasi[ $post_type ] ) ) {
$alt_dizin = $dizin_haritasi[ $post_type ];
$dirs['subdir'] = '/' . $alt_dizin;
$dirs['path'] = $dirs['basedir'] . '/' . $alt_dizin;
$dirs['url'] = $dirs['baseurl'] . '/' . $alt_dizin;
}
}
return $dirs;
}
Bu kodda $_REQUEST['post_id'] kullanımına dikkat edin. WordPress medya yükleyici, hangi post için yükleme yapıldığını bu parametre üzerinden iletir. Ancak her zaman absint() ile sanitize etmeyi unutmayın, güvenlik burada başlar.
wp-content Dışına Yükleme Yapmak
Bazı güvenlik politikaları, yüklenen dosyaların web kök dizini dışında saklanmasını zorunlu kılar. Bu yaklaşım özellikle hassas belgelerin yüklendiği sistemlerde kullanılır. Dosyayı sunucuya kaydet ama doğrudan URL ile erişilemez hale getir.
# Web kökü dışında bir dizin oluştur
mkdir -p /var/secure-uploads/belgeler
chown www-data:www-data /var/secure-uploads/belgeler
chmod 750 /var/secure-uploads/belgeler
add_filter( 'upload_dir', 'guvenli_yukleme_dizini' );
function guvenli_yukleme_dizini( $dirs ) {
// Sadece belirli bir özel post tipi için uygula
$current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
$dirs['basedir'] = '/var/secure-uploads/belgeler';
$dirs['baseurl'] = home_url( '/guvenli-dosya' ); // Proxy URL
$dirs['path'] = $dirs['basedir'] . $dirs['subdir'];
$dirs['url'] = $dirs['baseurl'] . $dirs['subdir'];
return $dirs;
}
Bu yaklaşımda URL kısmı gerçek bir fiziksel yola işaret etmez. Dosyaları sunmak için ayrı bir PHP scripti veya Nginx/Apache rewrite kuralı oluşturmanız ve dosyaya erişim için yetkilendirme kontrolü yapmanız gerekir.
Nginx tarafında dosya servisi için basit bir yapılandırma:
# /etc/nginx/sites-available/siteniz.conf içinde
location /guvenli-dosya {
alias /var/secure-uploads/belgeler;
internal; # Sadece PHP'nin X-Accel-Redirect ile yönlendirmesine izin ver
}
Çoklu Site (Multisite) Yapılarında Özel Dizin
WordPress Multisite kurulumlarında her alt site kendi sites/N/ dizinine yükler. Bu davranışı override etmek biraz daha dikkat gerektirir.
add_filter( 'upload_dir', 'multisite_ozel_yukleme' );
function multisite_ozel_yukleme( $dirs ) {
if ( ! is_multisite() ) {
return $dirs;
}
$blog_id = get_current_blog_id();
$blog_slug = get_blog_details( $blog_id )->path;
$blog_slug = trim( $blog_slug, '/' );
// Boş slug için ana site kontrolü
if ( empty( $blog_slug ) || $blog_slug === '/' ) {
$blog_slug = 'ana-site';
}
$temel_dizin = WP_CONTENT_DIR . '/network-uploads/' . $blog_slug;
$temel_url = WP_CONTENT_URL . '/network-uploads/' . $blog_slug;
$dirs['basedir'] = $temel_dizin;
$dirs['baseurl'] = $temel_url;
$dirs['path'] = $temel_dizin . $dirs['subdir'];
$dirs['url'] = $temel_url . $dirs['subdir'];
return $dirs;
}
Bu yapı, her sitenin yüklemelerini wp-content/network-uploads/site-slug/ altında toplar. Depolama yönetimi ve yedekleme açısından çok daha temiz bir yapı sunar.
Dinamik Dizin Seçimi: Tarih Yerine Kategori Kullanmak
Tarih tabanlı yapı yerine içerik kategorisine göre dizin oluşturmak isteyebilirsiniz. Bu özellikle fotoğraf galerisi veya dijital medya arşivi türündeki sitelerde işlevseldir.
add_filter( 'upload_dir', 'kategori_bazli_yukleme_dizini' );
function kategori_bazli_yukleme_dizini( $dirs ) {
$post_id = 0;
if ( isset( $_REQUEST['post_id'] ) ) {
$post_id = absint( $_REQUEST['post_id'] );
}
if ( $post_id > 0 ) {
$kategoriler = get_the_category( $post_id );
if ( ! empty( $kategoriler ) ) {
// İlk kategoriyi al, slug kullan
$kategori_slug = sanitize_file_name( $kategoriler[0]->slug );
// Üst kategori varsa onu da ekle
$ust_id = $kategoriler[0]->parent;
$yol = $kategori_slug;
if ( $ust_id > 0 ) {
$ust_kategori = get_category( $ust_id );
if ( ! is_wp_error( $ust_kategori ) ) {
$yol = sanitize_file_name( $ust_kategori->slug ) . '/' . $kategori_slug;
}
}
$dirs['subdir'] = '/' . $yol;
$dirs['path'] = $dirs['basedir'] . '/' . $yol;
$dirs['url'] = $dirs['baseurl'] . '/' . $yol;
}
}
return $dirs;
}
Bu kod, bir yazı “teknoloji/yazilim” kategorisindeyse yüklenen görseli uploads/teknoloji/yazilim/ içine koyar. Yüzlerce görselin bulunduğu bir sitede bu yapı, FTP veya dosya yöneticisi üzerinden gezinirken hayat kurtarır.
Yükleme Dizinini Önbellekleme Sorunlarına Karşı Koruma
upload_dir filtresinin her çağrıda tetiklendiğini ve bazı eklentilerin bu sonucu önbelleğe aldığını bilmek önemlidir. Kodunuzun beklenmedik sonuçlar üretmemesi için filtre kancasını dikkatli kullanın.
Ayrıca bazı durumlarda upload_dir filtresini yalnızca belirli bir işlem sırasında aktif etmek isteyebilirsiniz:
// Filtre sadece özel bir eylem sırasında aktif olsun
function ozel_yukleme_baslat() {
add_filter( 'upload_dir', 'gecici_ozel_dizin' );
}
add_action( 'pre_handle_404', 'ozel_yukleme_baslat' );
function ozel_yukleme_bitir() {
remove_filter( 'upload_dir', 'gecici_ozel_dizin' );
}
add_action( 'wp_handle_upload', 'ozel_yukleme_bitir' );
function gecici_ozel_dizin( $dirs ) {
$dirs['subdir'] = '/gecici';
$dirs['path'] = $dirs['basedir'] . '/gecici';
$dirs['url'] = $dirs['baseurl'] . '/gecici';
return $dirs;
}
Bu pattern, filtreni gerektiğinde ekleyip işlem bittikten sonra kaldırmanızı sağlar. Özellikle WooCommerce’in kendi yükleme süreçleriyle çakışmayı önlemek için bu yaklaşım tercih edilebilir.
Gerçek Dünya Senaryosu: Müşteri Bazlı Depolama
Ajans ortamlarında sıklıkla rastlanan bir gereksinim: Her müşterinin kendi yükleme dizininin olması. Özellikle tek bir WordPress kurulumu üzerinden birden fazla müşteriye hizmet verilen durumlarda bu yapı kaçınılmaz olur.
add_filter( 'upload_dir', 'musteri_bazli_yukleme_dizini' );
function musteri_bazli_yukleme_dizini( $dirs ) {
// Müşteri bilgisini özel bir seçenekten veya sayfanın URL'sinden al
$musteri_id = get_option( 'aktif_musteri_id', '' );
if ( empty( $musteri_id ) ) {
return $dirs;
}
$musteri_id = sanitize_key( $musteri_id );
$yil_ay = date( 'Y/m' );
$dirs['subdir'] = '/musteriler/' . $musteri_id . '/' . $yil_ay;
$dirs['path'] = $dirs['basedir'] . '/musteriler/' . $musteri_id . '/' . $yil_ay;
$dirs['url'] = $dirs['baseurl'] . '/musteriler/' . $musteri_id . '/' . $yil_ay;
// Dizin yoksa oluştur
if ( ! file_exists( $dirs['path'] ) ) {
wp_mkdir_p( $dirs['path'] );
}
return $dirs;
}
Bu yapıda aktif_musteri_id seçeneğini kullanıcı oturumuna, özel bir plugin ayarına veya URL parametresine göre dinamik olarak set edebilirsiniz.
Güvenlik Kontrolleri ve En İyi Pratikler
Özel yükleme dizinleri belirlerken güvenliği ön planda tutmak şart. İşte kaçınmamanız gereken kritik noktalar:
- Kullanıcı girdisini doğrulayın: Dizin yollarını oluştururken kullanıcıdan gelen her değeri
sanitize_file_name()veyasanitize_key()ile temizleyin. - Path traversal saldırılarına karşı koruyun:
../gibi dizin geçiş karakterlerini filtreleyin,realpath()ile gerçek yolu doğrulayın. - Yazma yetkilerini minimize edin: Web sunucusunun yalnızca belirli dizinlere yazma yetkisi olmasını sağlayın.
- Yürütme yetkisini engelleyin: Yükleme dizinlerine PHP veya diğer script dosyalarının çalıştırılmasını engelleyin.
Apache için .htaccess ile PHP çalıştırmayı engelleme:
# wp-content/uploads/.htaccess veya özel dizin içinde
<FilesMatch ".(php|php3|php4|php5|phtml|pl|py|jsp|asp|htm|shtml|sh|cgi)$">
deny from all
</FilesMatch>
Nginx için aynı amaçla nginx yapılandırması:
# nginx.conf veya site yapılandırmanızda
location ~* /uploads/.*.(php|php3|php4|php5|phtml|pl|py|jsp|asp)$ {
deny all;
return 403;
}
Mevcut Yüklemeleri Yeni Dizine Taşıma
Yükleme dizinini değiştirdikten sonra mevcut dosyaların yeni konuma taşınması gerekir. Bu işlemi elle yapmak yerine bir betikle halletmek daha güvenlidir:
#!/bin/bash
# Mevcut yüklemeleri yeni dizine taşı
ESKI_DIZIN="/var/www/html/wp-content/uploads"
YENI_DIZIN="/var/www/html/dosyalar"
# Yeni dizini oluştur
mkdir -p "$YENI_DIZIN"
# Dosyaları kopyala (önce kopyala, sonra sil)
rsync -av --progress "$ESKI_DIZIN/" "$YENI_DIZIN/"
# Sahiplik ve izinleri düzenle
chown -R www-data:www-data "$YENI_DIZIN"
find "$YENI_DIZIN" -type d -exec chmod 755 {} ;
find "$YENI_DIZIN" -type f -exec chmod 644 {} ;
echo "Taşıma tamamlandı."
Dosyaları taşıdıktan sonra veritabanındaki URL referanslarını da güncellemeniz gerekir. Bunun için WP-CLI’yi kullanmak en pratik yoldur:
# WP-CLI ile eski URL'leri yeni URL'lerle değiştir
wp search-replace 'wp-content/uploads' 'dosyalar' --all-tables --dry-run
# Dry-run sonucu tatmin ediciyse --dry-run parametresini kaldırın
wp search-replace 'wp-content/uploads' 'dosyalar' --all-tables
Sonuç
WordPress’te özel yükleme dizini belirlemek, upload_dir filtresini doğru kullanmaktan ibaret. Bu kadar güçlü bir mekanizmayı elinizin altında bulundurmak, site mimarinizi çok daha esnek hale getirir. Kullanıcı rolü bazlı ayrım, post tipine göre organizasyon, güvenlik gerektiren dosyalar için erişim kısıtlaması ya da multisite yapılarında merkezi depolama gibi senaryoların hepsini bu yöntemle karşılamak mümkün.
En önemli nokta şu: Hangi senaryoyu uygularsanız uygulayın, güvenlik kontrollerini atlamayın. Kullanıcı girdisini her zaman sanitize edin, dizin izinlerini doğru kurun ve yükleme dizinlerinde script çalıştırmayı engelleyin. Bu üç kural sağlamsa, geri kalan her şey teknik bir detaydan ibaret. Değişikliklerinizi her zaman bir geliştirme ortamında test edin, production’a geçmeden önce yedek alın ve WP-CLI’yi araç kutunuzdan eksik etmeyin.
