Admin Paneli Yavaş Açılıyor: WordPress Heartbeat Optimizasyonu
WordPress admin panelini açtığınızda kahvenizi içip geri gelseniz bile hâlâ yükleniyorsa, suçlu büyük ihtimalle WordPress Heartbeat API‘dir. Bu yazıda heartbeat’in ne yaptığını, neden bu kadar kaynak tükettiğini ve functions.php üzerinden nasıl optimize edeceğinizi gerçek dünya senaryolarıyla ele alacağız.
WordPress Heartbeat API Nedir?
WordPress 3.6 ile gelen Heartbeat API, tarayıcı ile sunucu arasında düzenli aralıklarla AJAX istekleri göndererek canlı iletişim kurar. Bu sayede şu özellikler çalışır:
- Otomatik kaydetme: Yazı düzenlerken içerik otomatik kaydedilir
- Post locking: Birden fazla kullanıcı aynı yazıyı düzenlemeye çalışırsa uyarı verilir
- Oturum kontrolü: Kullanıcının giriş durumu kontrol edilir
- WooCommerce stok güncellemeleri: Gerçek zamanlı stok takibi
Kulağa güzel geliyor, değil mi? Sorun şu ki bu sistem varsayılan olarak 15-60 saniyede bir sunucuya istek atar. 10 sekme açık bir admin, 5 farklı kullanıcı, üzerine bir de eklentilerin kendi heartbeat kancaları… Sunucunuz wp-admin/admin-ajax.php dosyasına dakikada onlarca istek alır hale gelir.
Sorunu Gözlemlemek
Optimizasyona geçmeden önce sorunu somutlaştıralım. Sunucunuzda şu komutu çalıştırın:
tail -f /var/log/nginx/access.log | grep "admin-ajax.php"
Eğer her birkaç saniyede bir satır geliyorsa, heartbeat tam gaz çalışıyor demektir. Daha detaylı analiz için:
grep "admin-ajax.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
Bu komut son log dosyasında hangi IP’nin admin-ajax’a kaç kez istek attığını gösterir. Bir IP’den yüzlerce istek görüyorsanız, o admin kullanıcısının tarayıcısı çıldırmış demektir.
PHP-FPM worker durumuna da göz atın:
php-fpm -t && cat /etc/php/8.1/fpm/pool.d/www.conf | grep "pm.max_children"
Heartbeat’in PHP worker’larını meşgul ettiğini görmek için:
watch -n 1 "ps aux | grep php-fpm | wc -l"
Temel Heartbeat Kontrol Fonksiyonları
Şimdi asıl işe gelelim. Tüm bu kodları functions.php dosyanıza ekleyeceksiniz. Ama dikkat: child theme kullanıyorsanız child theme’in functions.php dosyasını kullanın, aksi takdirde tema güncellemesinde kodlarınız uçar.
1. Heartbeat’i Tamamen Devre Dışı Bırakmak
En agresif çözüm, heartbeat’i komple kapatmak:
// functions.php
add_action('init', function() {
wp_deregister_script('heartbeat');
}, 1);
Bu yöntem sunucuyu rahatlatır ama yukarıda saydığımız özelliklerin tamamını devre dışı bırakır. Tek kullanıcılı siteler veya içerik düzenlemenin kritik olmadığı showcase siteler için idealdir. WooCommerce kullanan bir site için önermiyorum.
2. Sadece Frontend’de Devre Dışı Bırakmak
Çoğu durumda zaten akıllıca olan yöntem: Heartbeat’e sadece admin panelinde izin vermek.
add_action('init', function() {
if (!is_admin()) {
wp_deregister_script('heartbeat');
}
}, 1);
Bu kod ile ziyaretçi trafiğinden gelen gereksiz AJAX yükünü tamamen ortadan kaldırırsınız. Admin kullanıcıları otomatik kaydetme gibi özelliklerden yararlanmaya devam eder.
3. Heartbeat Frekansını Azaltmak
Heartbeat’i kapatmak istemiyorsanız ama daha seyrek çalışmasını istiyorsanız:
add_filter('heartbeat_settings', function($settings) {
// Varsayilan 15-60 saniye yerine 120 saniye yapiyoruz
$settings['interval'] = 120;
return $settings;
});
interval değeri saniye cinsindendir ve minimum 15 olabilir (WordPress bu değeri 15’in altına düşürmenize izin vermez, izin verse bile tarayıcı zaten bu kadar sık istek yapmak istemez).
Peki ya sadece post editörde daha sık, diğer admin sayfalarında daha seyrek çalışmasını istiyorsanız?
add_filter('heartbeat_settings', function($settings) {
global $pagenow;
// Post duzenleme sayfasinda 45 saniye
if ($pagenow === 'post.php' || $pagenow === 'post-new.php') {
$settings['interval'] = 45;
} else {
// Diger admin sayfalarda 120 saniye
$settings['interval'] = 120;
}
return $settings;
});
Bu yaklaşım en dengeli sonucu verir: Yazarlar otomatik kaydetme özelliğini makul aralıklarla kullanır, dashboard ve diğer sayfalar gereksiz yere sunucuyu meşgul etmez.
Sayfa Bazlı Gelişmiş Kontrol
Gerçek dünyada her sayfa aynı değildir. WooCommerce siparişler listesi açıkken heartbeat’e ihtiyaç varken, tema özelleştirici açıkken belki daha az isteyebilirsiniz. İşte daha sofistike bir yapı:
add_action('admin_enqueue_scripts', function($hook) {
// WooCommerce siparis sayfasinda heartbeat aktif kalsin ama yavas olsun
if (in_array($hook, ['edit.php', 'woocommerce_page_wc-orders'])) {
add_filter('heartbeat_settings', function($settings) {
$settings['interval'] = 60;
return $settings;
});
return;
}
// Dashboard'da cok yuksek frekansta calismasi gerekmez
if ($hook === 'index.php') {
add_filter('heartbeat_settings', function($settings) {
$settings['interval'] = 90;
return $settings;
});
return;
}
// Diger butun sayfalarda cok daha yavas
add_filter('heartbeat_settings', function($settings) {
$settings['interval'] = 180;
return $settings;
});
}, 99);
WooCommerce için Özel Heartbeat Yönetimi
WooCommerce mağazaları çalıştırıyorsanız işler biraz daha karmaşıklaşır. WooCommerce’in stok ve sipariş yönetimi heartbeat’e bağımlıdır. Bu yüzden tamamen kapatmak yerine akıllıca yönetmek şart.
add_filter('heartbeat_settings', function($settings) {
global $pagenow, $typenow;
// WooCommerce urun duzenleme: orta frekan
if ($pagenow === 'post.php' && $typenow === 'product') {
$settings['interval'] = 60;
return $settings;
}
// WooCommerce siparis duzenleme: daha sik, stok kritik
if ($pagenow === 'post.php' && $typenow === 'shop_order') {
$settings['interval'] = 30;
return $settings;
}
// Genel yazi duzenleme
if ($pagenow === 'post.php' || $pagenow === 'post-new.php') {
$settings['interval'] = 45;
return $settings;
}
// Her sey disindaki admin sayfalari
$settings['interval'] = 120;
return $settings;
});
Bununla birlikte, frontend’de WooCommerce heartbeat’ini kapatmayı da unutmayın:
add_action('wp_enqueue_scripts', function() {
// Sepet ve odeme sayfasi disinda frontend heartbeat'i kapat
if (!is_cart() && !is_checkout()) {
wp_deregister_script('heartbeat');
}
}, 99);
Kullanıcı Rolüne Göre Heartbeat Yönetimi
Editörler ve aboneler için heartbeat’i farklı yapılandırmak mantıklı bir yaklaşımdır. Yöneticilerin daha sık senkronizasyona ihtiyacı olabilirken, sadece yazı yazan editörler için daha seyrek aralıklar yeterlidir:
add_filter('heartbeat_settings', function($settings) {
$user = wp_get_current_user();
if (!$user || !$user->ID) {
return $settings;
}
// Yonetici icin makul bir ayar
if (in_array('administrator', $user->roles)) {
$settings['interval'] = 60;
return $settings;
}
// Editor icin biraz daha yuksek aralik
if (in_array('editor', $user->roles)) {
$settings['interval'] = 90;
return $settings;
}
// Author ve diger roller icin en yuksek aralik
$settings['interval'] = 120;
return $settings;
});
Gerçek Dünya Senaryosu: Shared Hosting Kabusu
Bir müşterimin sitesini ele alalım. Orta boy bir e-ticaret sitesi, shared hosting üzerinde çalışıyor, WordPress 6.x, WooCommerce ve 23 eklenti aktif. Admin paneli açılışı 8-12 saniye sürüyor.
İlk incelemede şunu bulduk:
grep "admin-ajax.php" /var/log/apache2/access.log | wc -l
# Cikti: 4782 (son 1 saatte)
Saatte neredeyse 5000 admin-ajax isteği. 3 admin kullanıcısı vardı ve her biri birden fazla sekme açıktı. Yaptığımız değişiklikler:
// 1. Frontend'de tamamen kapat
add_action('init', function() {
if (!is_admin()) {
wp_deregister_script('heartbeat');
}
}, 1);
// 2. Admin'de sayfa bazli optimize et
add_filter('heartbeat_settings', function($settings) {
global $pagenow;
$editing_pages = ['post.php', 'post-new.php'];
if (in_array($pagenow, $editing_pages)) {
$settings['interval'] = 60;
} else {
$settings['interval'] = 180;
}
return $settings;
});
Sonuç olarak aynı saatte admin-ajax sayısı 340’a düştü. Admin panel yükleme süresi 2-3 saniyeye geriledi. Shared hosting üzerinde bu dramatik bir fark.
Heartbeat ile Birlikte Yapılması Gerekenler
Heartbeat optimizasyonu tek başına yeterli değildir. Gerçek bir performans kazanımı için şunları da yapmanız gerekir.
Object cache etkinleştirin. Redis veya Memcached kullanıyorsanız admin-ajax istekleri çok daha hızlı yanıt verir:
# Redis bagli PHP testi
php -r "
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo $redis->ping();
"
admin-ajax.php isteklerini PHP-FPM ile izleyin:
# PHP-FPM slow log ayari
; /etc/php/8.1/fpm/pool.d/www.conf dosyasina ekleyin
; slowlog = /var/log/php-fpm-slow.log
; request_slowlog_timeout = 5s
# Sonra izleyin
tail -f /var/log/php-fpm-slow.log
Gereksiz heartbeat hook’larını temizleyin. Bazı eklentiler heartbeat’e kendi işlemlerini ekler. Hangi eklentinin ne eklediğini görmek için:
add_action('heartbeat_received', function($response, $data) {
// Debug modunda hangi veri geliyor goster
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('Heartbeat data: ' . print_r($data, true));
}
return $response;
}, 999, 2);
Bu kodu geçici olarak ekleyin, debug log’unuzu inceleyin ve hangi eklentinin heartbeat’e ne gönderdiğini anlayın. Sonra kodu kaldırın.
Nonce ve Güvenlik Notları
Heartbeat API, WordPress’in nonce sistemiyle çalışır. Bazı geliştiriciler heartbeat’i kapatırken nonce’ların eskimesi (expiration) sorunuyla karşılaşır. Eğer kullanıcılarınız “oturumunuz sona erdi” hataları alıyorsa, heartbeat’i tamamen kapatmadan önce nonce süresini uzatmayı deneyin:
add_filter('nonce_life', function() {
// Varsayilan 24 saat yerine 48 saat
return 48 * HOUR_IN_SECONDS;
});
Bu filtreyi heartbeat optimizasyonuyla birlikte kullanmak, oturum sorunlarının önüne geçer.
Çoklu Site (Multisite) Kurulumları
WordPress Multisite kullanıyorsanız heartbeat her alt site için ayrı ayrı çalışır. Network genelinde tek bir ayar yapmak için network-wide bir eklenti veya sunrise.php kullanmak gerekir. Ama en pratik yol, her sitenin functions.php‘sine aynı kodu eklemek yerine bir mu-plugins dosyası oluşturmaktır:
# /wp-content/mu-plugins/heartbeat-control.php dosyasi olusturun
<?php
/**
* Plugin Name: Heartbeat Kontrolu
* Description: Tum sitelerde heartbeat optimizasyonu
*/
add_filter('heartbeat_settings', function($settings) {
global $pagenow;
if (in_array($pagenow, ['post.php', 'post-new.php'])) {
$settings['interval'] = 60;
} else {
$settings['interval'] = 120;
}
return $settings;
});
add_action('init', function() {
if (!is_admin()) {
wp_deregister_script('heartbeat');
}
}, 1);
Bu dosya mu-plugins klasörüne koyulduğunda ağdaki tüm sitelere otomatik uygulanır, aktifleştirme gerekmez.
Değişikliklerin Etkisini Ölçmek
Yaptığınız değişikliklerin işe yarayıp yaramadığını nasıl anlarsınız? Önce bir baseline oluşturun:
# Degisiklik oncesi 10 dakikalik admin-ajax sayisi
grep "admin-ajax.php" /var/log/nginx/access.log |
awk -v d="$(date -d '10 minutes ago' '+%d/%b/%Y:%H:%M')"
'$4 > "["d' | wc -l
Değişiklikleri yaptıktan sonra aynı komutu çalıştırın ve karşılaştırın. Tarayıcı tarafında da Chrome DevTools’un Network sekmesini açık bırakıp admin-ajax.php isteklerini filtreleyin. İstekler arasındaki sürenin arttığını gözlemleyebilirsiniz.
Sonuç
WordPress Heartbeat API küçük bir özellik gibi görünse de yanlış yapılandırıldığında sunucunuzu dizlerine getirebilecek bir AJAX bombardımanına dönüşür. Özellikle shared hosting, küçük VPS’ler veya yüksek admin kullanıcısı olan sitelerde etkisi dramatiktir.
Önerilen yaklaşımı şöyle özetleyebilirim: Frontend’de tamamen kapatın, editör sayfalarında 45-60 saniyeye ayarlayın, diğer admin sayfalarında 120-180 saniyeye çıkarın. WooCommerce kullanıyorsanız sipariş ve ürün sayfalarına özel kurallar ekleyin.
Bu optimizasyonlar tek başına admin panel açılış süresini genellikle yüzde 40 ile 70 arasında iyileştirir. Buna object cache, PHP-FPM fine-tuning ve eklenti optimizasyonunu da eklediğinizde, kullanıcılarınız artık yavaş admin panelinden şikayet etmek yerine sizin diğer sorunlarınızı bulup getirirler. Ki bu, en azından ilerleme demektir.
