Gelen kutunuzdaki e-postanın konusu her gün aynı: "Hesabınız CPU limitini aştı." cPanel'de Metrics → Resource Usage ekranını açıyorsunuz, son 24 saatte onlarca "CPU limitine takıldı" olayı görünüyor. Site tamamen çökmüyor ama panel hantallaşmış, yazı kaydederken kilitleniyor, arada ziyaretçiler 508 hatası alıyor. Trafik ise geçen aya göre neredeyse değişmemiş.
Erişim logunu açtığınızda tabloyu tek bir satır domine ediyor: POST /wp-admin/admin-ajax.php. Binlerce kez. Üstelik gecenin üçünde bile, ziyaretçi yokken. Bu noktada internetin ilk verdiği tavsiye hep aynıdır: "Heartbeat API'yi kapat." Bu tavsiye çoğu zaman işe yarar — ve çoğu zaman da otomatik kaydı, yazı kilidini ve panelin canlı bildirimlerini beraberinde götürür. Bir hafta sonra bir editörünüz 40 dakikalık yazısını kaybeder, kimse de bunu bir ay önceki performans ayarıyla ilişkilendiremez.
Bu rehber iki bölümden oluşuyor. Önce teşhis: erişim logundaki admin-ajax.php isteklerini sayıp bunları kaynak kullanım raporundaki limit aşımı anlarıyla eşleştireceğiz; çünkü bu isteklerin CPU'yu gerçekten doldurup doldurmadığı kanıtlanmadan yapılan her müdahale körlemesine olur. Sonra doğru doz: neyi kapatıp neyi bırakacağınızı, hangi ekranda hangi aralığın makul olduğunu ve değişikliğin işe yarayıp yaramadığını nasıl ölçeceğinizi göreceğiz.
admin-ajax.php Nedir ve Heartbeat Onu Neden Sürekli Çağırır#
wp-admin/admin-ajax.php, WordPress'in genel amaçlı AJAX uç noktasıdır. Tarayıcı tarafındaki her şey — panelin canlı bildirimleri, eklentilerin arka plan sorguları, temaların "daha fazla yükle" butonları — sunucuyla konuşmak için buraya POST atar. Sorun uç noktanın kendisi değil, maliyetidir: admin-ajax.php her çağrıldığında WordPress çekirdeği, aktif tüm eklentiler ve temanın functions.php dosyası baştan yüklenir. Yani tek bir "ping" isteği, CPU açısından tam bir sayfa oluşturma işlemine yakın masraf çıkarır.
İşin can alıcı kısmı şu: bu istekler önbelleğe hiç uğramaz. LiteSpeed Cache, WP Rocket ya da Cloudflare sayfa önbelleği, POST isteklerini ve oturum açmış kullanıcıları atlar. Ön yüzde ziyaretçi başına 1 PHP çalıştırması yapan iyi optimize edilmiş bir sitede, arka planda dakikada 4 kez tetiklenen bir Heartbeat isteği rahatlıkla toplam PHP yükünün yarısını oluşturabilir. Önbellek eklentisi eklemek bu tabloyu değiştirmez; konuyu WordPress önbellek rehberi yazımızda ayrıntılandırıyoruz.
Heartbeat API, WordPress 3.6 ile gelen ve tarayıcı ile sunucu arasında düzenli bir nabız kuran mekanizmadır. Otomatik kayıt, yazı kilidi ("Bu yazıyı şu anda Ayşe düzenliyor"), oturum süresi uyarısı ve birçok eklentinin canlı sayaçları bu nabzın üstünde çalışır. Varsayılan tempo şöyledir:
| Ekran | Varsayılan aralık | Ne için kullanılıyor |
|---|---|---|
Yazı/sayfa düzenleyici (post.php, post-new.php) | 15 saniye | Otomatik kayıt, yazı kilidi, oturum kontrolü |
| Panelin diğer ekranları | 60 saniye | Bildirimler, eklenti sayaçları |
| Kullanıcı 5 dakikadır hareketsizse | 120 saniye ("slow" mod) | Sekme açık ama kullanılmıyor |
| Ön yüz | Çekirdek yüklemez | Yalnızca bir eklenti/tema talep ederse çalışır |
Rakama dökelim. Bir editör sabah bir yazı açıp sekmeyi kapatmadan 8 saat çalışırsa, 15 saniyelik aralıkla yaklaşık 1.900 istek üretir. Üç editör aynı anda çalışıyorsa günlük 5.000-6.000 tam WordPress yüklemesi eder. Paylaşımlı bir pakette bu tek başına aylık CPU bütçesinin ciddi bir bölümüdür — üstelik bu isteklerin hiçbiri ziyaretçiye tek bir sayfa göstermez.
Önce Teşhis: Erişim Logunda admin-ajax.php İsteklerini Saymak#
Suçluyu ilan etmeden önce sayın. cPanel'de ham erişim logları ~/access-logs/ dizininde, alan adı başına bir dosya olarak durur. SSH erişiminiz yoksa Metrics → Raw Access üzerinden indirip yerelde de aynı komutları çalıştırabilirsiniz.
cd ~/access-logs
# 1) Günün toplam admin-ajax isteği kaç tane?
grep -c 'admin-ajax\.php' ornek.com
# 2) Toplam istek sayısıyla kıyasla: oran ne?
wc -l < ornek.com
# 3) Dakika bazında dağılım — en yoğun 10 dakika
awk '/admin-ajax\.php/ {split($4, t, ":"); print t[2] ":" t[3]}' ornek.com \
| sort | uniq -c | sort -rn | head -10
İlk iki komutun oranı tek başına çok şey söyler. admin-ajax.php istekleri toplamın %5'i ise sorun büyük ihtimalle başka yerdedir. %40'ı ise doğru izdesiniz.
Üçüncü komut, dağılımın şeklini verir ve bu şekil teşhisin yarısıdır. Heartbeat trafiği testere dişi gibidir: dakikada 4-8 istek, saatlerce sabit, gece gündüz farkı editörlerin mesai saatiyle örtüşür. Bir bot saldırısı ise ani bir duvar oluşturur — beş dakika boyunca dakikada 600 istek, sonra sıfır.
Kimin ürettiğini görmek için IP kırılımına bakın:
grep 'admin-ajax\.php' ornek.com | awk '{print $1}' \
| sort | uniq -c | sort -rn | head
Tek bir IP'den binlerce istek geliyorsa iki ihtimal var: ya o IP sizin ofisinizin çıkışıdır (yani sizin editörünüz), ya da dışarıdan bir bot. Ayırmak için o IP'nin zaman damgalarındaki ritme bakın:
grep 'admin-ajax\.php' ornek.com | grep '^203\.0\.113\.5 ' \
| awk '{print $4}' | tail -20
Saniyeler tam 15'in ya da 60'ın katlarıyla ilerliyorsa elinizde Heartbeat var. Düzensiz ve çok daha sıksa bir eklentinin yoklaması ya da bir saldırı söz konusudur.
Hangi Ekrandan Geldiğini Bulmak#
Buradaki en yaygın hayal kırıklığı şudur: admin-ajax.php çağrılarında hangi işlemin yapıldığını belirten action parametresi POST gövdesindedir, erişim logunda görünmez. Yani logdan doğrudan "bu istek heartbeat'ti" diyemezsiniz. Ama referrer alanı sizi ekrana kadar götürür:
grep 'admin-ajax\.php' ornek.com | awk -F'"' '{print $4}' \
| sort | uniq -c | sort -rn | head
Çıktıda post.php?post=1234&action=edit gibi adresler baskınsa kaynak düzenleyicidir, yani Heartbeat'tir. /wp-admin/index.php ise panelin ana ekranıdır. Referrer boşsa ve kullanıcı ajanı da boş ya da tuhafsa, oturum açmamış bir istemci — büyük olasılıkla bot — doğrudan uç noktaya vuruyordur.
En kesin yöntem tarayıcıdadır: panelde bir yazı düzenleme ekranı açın, geliştirici araçlarında Network sekmesini admin-ajax ile filtreleyin ve iki dakika bekleyin. İstekleri sayın, birinin gövdesine tıklayıp action: heartbeat satırını ve yanıt süresini görün. 300 ms'nin üstünde bir yanıt süresi, her nabzın gerçek bir CPU masrafı olduğunu gösterir.
Bu İstekler Gerçekten CPU'yu mu Dolduruyor?#
Sayı elinizde; şimdi kaynak raporuyla eşleştirin. cPanel'de Metrics → Resource Usage → Details ekranı, limit aşımlarının saatini ve hangi limitin (CPU, giriş süreçleri, I/O, bellek) dolduğunu gösterir. Bu ekranın nasıl okunduğunu cPanel kaynak kullanımı izleme yazımızda adım adım anlatıyoruz.
Aradığınız şey örtüşmedir:
- Limit aşımı saatleri,
admin-ajax.phpyoğunluğunun zirve yaptığı dakikalarla çakışıyorsa teşhis doğrulanmıştır. - Aşımlar gece 04:00'te oluyor ama admin-ajax trafiği o saatte sıfırsa, suçlu başkasıdır: yedekleme görevi,
wp-cron, arama motoru taraması ya da veritabanı optimizasyonu. Bu durumda Heartbeat'e dokunmak hiçbir şeyi düzeltmez. - Dolan limit CPU değil de giriş süreçleri (entry processes) ise, tablo biraz farklıdır: eşzamanlı PHP isteği sayısı tavana vuruyordur ve Heartbeat bunu tetikleyen damla olabilir. 508 hatasının bu iki limitle ilişkisini 508 resource limit is reached hatası yazısında ele aldık.
Kendi sunucunuzu yönetiyorsanız (VDS/VPS) aynı eşleştirmeyi top, htop ve PHP-FPM durum sayfasıyla yaparsınız. Yük ortalamasının çekirdek sayısına göre nasıl yorumlanacağı için Linux load average'ı anlamak yazısına göz atın. Paylaşımlı pakette hangi limitlerin sizi bağladığını hatırlamak içinse paylaşımlı hosting kaynak limitleri yazısı iyi bir başvuru noktasıdır.
Heartbeat'i Tamamen Kapatmanın Bedeli#
Kapatma önerisi her yerde dolaştığı için bedelini net yazalım. Heartbeat'i tamamen devre dışı bıraktığınızda şunlar çalışmaz hale gelir:
- Otomatik kayıt. WordPress yazınızı arka planda 60 saniyede bir kaydeder ve bunu Heartbeat üzerinden yapar. Nabız yoksa yalnızca "Taslağı kaydet" butonuna bastığınız an kaydedilirsiniz. Tarayıcı çökerse aradaki her şey gider.
- Yazı kilidi. İki editör aynı yazıyı açtığında "Bu yazı şu anda düzenleniyor" uyarısı çıkmaz. İkisi de kaydeder, biri diğerinin üzerine yazar ve kimse ne olduğunu anlamaz.
- Oturum uyarısı. Oturumunuz düşünce çıkan "Bağlantı kesildi, yazınız kaydedilmiyor" uyarısı gelmez. Yazıyı yazar, kaydet dersiniz, giriş ekranına düşersiniz.
- Eklentilerin canlı ekranları. WooCommerce'in yeni sipariş bildirimi, form eklentilerinin canlı gönderi sayacı, sipariş ekranındaki durum güncellemeleri sessizce durur. Hata vermezler; sadece hiçbir şey olmaz — teşhisi en zor arıza türü budur.
Bu yüzden varsayılan tavsiyemiz kapatmak değil, temposunu düşürmek ve ihtiyaç olmayan yerde kaldırmaktır. Kapatmanın savunulabilir olduğu tek yer, kimsenin yazı yazmadığı ekranlardır.
Doğru Doz: Üç Kademeli Ayar#
Heartbeat'i tek anahtarla değil, üç ayrı bağlamda ele alın:
- Ön yüz: Çekirdek zaten yüklemez; bir eklenti yüklüyorsa da ziyaretçiye faydası yoktur. Buradan kaldırmak risksizdir.
- Yazı düzenleyici: Otomatik kayıt ve kilit burada yaşar. Aralığı 15'ten 30-60 saniyeye çıkarmak makul; kapatmak değil.
- Panelin geri kalanı: Bildirim sayaçlarının 2 dakikada bir güncellenmesi kimseyi rahatsız etmez.
Ayarı temaya değil, bir mu-plugin dosyasına yazın. wp-content/mu-plugins/heartbeat-ayari.php (dizin yoksa oluşturun) altındaki kod tema güncellemelerinden ve tema değişikliğinden etkilenmez, otomatik olarak etkinleşir:
<?php
/**
* Plugin Name: Heartbeat Ayarı
* Description: admin-ajax.php üzerindeki Heartbeat yükünü bağlama göre sınırlar.
*/
// 1) Ön yüzde Heartbeat'i tamamen kaldır.
add_action( 'init', function () {
if ( ! is_admin() ) {
wp_deregister_script( 'heartbeat' );
}
}, 1 );
// 2) Panelde aralığı ekrana göre ayarla.
add_filter( 'heartbeat_settings', function ( $settings ) {
global $pagenow;
$editor_ekranlari = array( 'post.php', 'post-new.php', 'site-editor.php' );
if ( in_array( $pagenow, $editor_ekranlari, true ) ) {
$settings['interval'] = 30; // otomatik kayıt ve kilit çalışmaya devam etsin
} else {
$settings['interval'] = 120; // bildirim sayaçları için fazlasıyla yeterli
}
return $settings;
} );
Bilmeniz gereken bir sınır var: tarayıcı tarafındaki heartbeat.js, kendisine verilen aralığı 15 ile 120 saniye arasına kırpar. 300 yazarsanız 120 olarak uygulanır; 5 yazarsanız 15'e yükseltilir. Yani "aralığı 10 dakika yaptım ama istekler devam ediyor" şikâyetinin sebebi genellikle budur, filtrenin çalışmaması değil.
Aşağıdaki tablo, farklı site tipleri için pratikte iyi sonuç veren değerleri özetliyor:
| Site tipi | Ön yüz | Yazı düzenleyici | Panelin geri kalanı |
|---|---|---|---|
| Tek yazarlı blog / kurumsal site | Kapalı | 60 sn | 120 sn |
| Çok yazarlı haber sitesi | Kapalı | 15-30 sn (kilit kritik) | 60 sn |
| WooCommerce mağaza | Kapalı | 30 sn | 60 sn (sipariş ekranı canlı kalsın) |
| Ajans / staging kopyası | Kapalı | 60 sn | 120 sn |
Çok yazarlı bir yayında editör aralığını uzatırken dikkatli olun: 120 saniyeye çıkarırsanız, iki editörün aynı yazıyı açması durumunda kilit uyarısının gelmesi de 2 dakika gecikir. Kaybedilen 2 dakikalık emek, kazanılan CPU'dan pahalıya gelebilir.
Kod Yazmak İstemiyorsanız: Eklenti Seçenekleri#
Aynı üç kademeyi arayüzden yöneten araçlar var. Heartbeat Control eklentisi ön yüz, panel ve düzenleyici için ayrı ayrı "izin ver / aralığı değiştir / devre dışı bırak" seçeneği sunar; yukarıdaki kodun birebir karşılığıdır. Halihazırda LiteSpeed Cache veya WP Rocket kullanıyorsanız ikisinde de yerleşik bir Heartbeat bölümü bulunur — LiteSpeed tarafındaki ayarların yerini LiteSpeed Cache rehberimizde bulabilirsiniz.
Tek bir uyarı: ikisini birden kullanmayın. Hem mu-plugin'de filtre tanımlayıp hem de bir eklentiden ayar yaparsanız, son çalışan filtre kazanır ve panelde gördüğünüz değerle tarayıcıya giden değer birbirini tutmaz. Bu tür sessiz çakışmaları ayıklamanın yöntemini WordPress eklenti çakışması yazısında anlattık. Hangisini seçerseniz seçin, uygulanan gerçek ayarı WP-CLI ile doğrulayın:
wp eval 'print_r( apply_filters( "heartbeat_settings", array() ) );'
Heartbeat Değilse: admin-ajax.php'yi Başka Kim Çağırıyor#
Aralığı uzattınız ama istek sayısı düşmediyse, kaynağı yanlış teşhis etmişsiniz demektir. Sık karşılaşılan diğer üreticiler:
- WooCommerce sepet parçacıkları. Ön yüzde her sayfa yüklemesinde
?wc-ajax=get_refreshed_fragmentsçağrılır. Teknik olarakadmin-ajax.phpdeğildir ama maliyeti aynıdır ve loglarda benzer görünür. Sepet sayacını başlıkta göstermiyorsanız kapatılabilir. - Sayfa oluşturucular. Elementor, WPBakery ve benzeri araçlar düzenleme ekranında yoğun AJAX trafiği üretir. Bu trafik yalnızca siz düzenleme yaparken oluşur; logda mesai saatlerine sıkışmış yığınlar halinde görünür.
wp_ajax_nopriv_uçlarına gelen bot trafiği. Bazı eklentiler oturum açmamış ziyaretçilere de AJAX uçları açar. Botlar bu uçları bulur ve saniyede onlarca POST atar. Her istek tam bir WordPress yüklemesidir; hiçbir önbellek araya giremez.- Karışıklık:
wp-cron.php. Zamanlanmış görevler ayrı bir dosyadan çalışır. Logdaadmin-ajax.phpdeğilwp-cron.phpgörüyorsanız çözüm de tamamen farklıdır (gerçek cron'a taşımak).
Bot kaynaklı trafikte referrer boş, kullanıcı ajanı ya boştur ya da tek tip bir dizedir. Bu ayrımı yaptıktan sonra sunucu tarafında fren koyabilirsiniz.
Sunucu Tarafında Fren: Oturum Açmamış Trafiği Sınırlama#
Kendi Nginx sunucunuzu yönetiyorsanız, oturum açmış kullanıcıları muaf tutan bir hız sınırı kurabilirsiniz. Buradaki püf nokta, wordpress_logged_in_ çerezi taşıyan isteklere boş anahtar atamaktır; Nginx boş anahtarlı istekleri sınırlamaz:
map $http_cookie $ajax_limit_key {
default $binary_remote_addr;
"~*wordpress_logged_in_" "";
}
limit_req_zone $ajax_limit_key zone=wpajax:10m rate=20r/m;
location = /wp-admin/admin-ajax.php {
limit_req zone=wpajax burst=10 nodelay;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Paylaşımlı hostingte Nginx yapılandırmasına erişemezsiniz; oradaki karşılığı Cloudflare'de /wp-admin/admin-ajax.php yoluna, oturum çerezi olmayan istekler için tanımlanan bir rate limiting kuralıdır.
Bu bölümde bir uyarı şart: admin-ajax.php dosyasını .htaccess ile tamamen kapatmayın. İletişim formları, filtrelemeli ürün listeleri, yorum gönderimi ve arama önerileri dahil pek çok ön yüz özelliği bu uç noktadan geçer. Kapatınca site "çalışıyor" görünür ama formlar sessizce boşa gider — kullanıcı gönder der, hiçbir şey olmaz.
Değişikliği Doğrulama: Önce ve Sonra Ölçümü#
Müdahaleyi yaptıktan sonra aynı üç ölçümü tekrarlayın; "sanırım düzeldi" bir sonuç değildir.
| Ölçüm | Nasıl bakılır | Beklenen değişim |
|---|---|---|
| Panelde 5 dakikalık istek sayısı | DevTools → Network → admin-ajax filtresi | 20 istekten 2-3 isteğe |
| Günlük toplam admin-ajax | grep -c 'admin-ajax\.php' ~/access-logs/ornek.com | %60-85 azalma |
| Limit aşımı sayısı | cPanel → Resource Usage → Details, 48 saat | Sıfıra ya da tek haneye |
| Ortalama yanıt süresi | Aynı ekranda birkaç isteğin Timing sekmesi | Değişmez (istek sayısı düşer, maliyet aynı) |
Karşılaştırmayı aynı gün ve saat aralığında yapın. Pazartesi 14:00 ile Pazar 03:00'ü kıyaslarsanız kendinizi kandırırsınız.
İstek sayısı düştüğü halde CPU aşımları sürüyorsa Heartbeat sizin darboğazınız değildi. O noktada sıra genel performans denetimindedir: sorgu sayısı, eklenti yükü, önbellek kapsamı ve görsel boyutları. Bu adımların sırasını WordPress hız optimizasyonu rehberinde topladık.
Son bir not: bu ayarları yaptıktan sonra bir yere yazın. Altı ay sonra "otomatik kayıt neden 30 saniyede bir çalışıyor" diye soran biri olacak ve mu-plugins dizinindeki tek satırlık bir yorum, bir saatlik araştırmayı önleyecek.
Sıkça Sorulan Sorular#
Heartbeat API'yi tamamen kapatmak zararlı mı?#
Zararlı değil ama bedelli. Kapattığınızda otomatik kayıt, yazı kilidi ve oturum düşme uyarısı çalışmaz; eklentilerin canlı bildirim sayaçları sessizce durur. Tek yazarlı, günde birkaç yazı girilen bir sitede bu bedel katlanılabilir. Birden fazla editörün aynı içeriklere dokunduğu yerlerde ise üzerine yazma kazaları başlar. Önerilen yol, kapatmak yerine aralığı uzatmak ve yalnızca ön yüzde kaldırmaktır.
admin-ajax.php isteklerinin hepsi Heartbeat mi?#
Hayır. Bu uç nokta bütün eklentilerin ortak AJAX kapısıdır. Sepet güncellemeleri, iletişim formları, sayfa oluşturucuların düzenleme ekranı ve oturum açmamış ziyaretçilere açılan nopriv uçları da buradan geçer. Erişim logundaki referrer alanı size kaynağı gösterir: düzenleme ekranı adresleri Heartbeat'i, boş referrer ile tek tip kullanıcı ajanı ise bot trafiğini işaret eder.
Heartbeat aralığını en fazla kaç saniye yapabilirim?#
Pratik üst sınır 120 saniyedir. Tarayıcıdaki heartbeat.js kendisine verilen değeri 15 ile 120 saniye arasına kırptığı için, filtreye 300 yazsanız da uygulanan değer 120 olur. "Aralığı çok uzattım ama istekler azalmadı" şikâyetinin en yaygın sebebi budur. Daha fazla azaltma istiyorsanız aralığı değil, Heartbeat'in yüklendiği ekranların sayısını düşürmelisiniz.
Ziyaretçi yokken bile ön yüzde admin-ajax istekleri görüyorum, sebebi ne?#
İki tipik kaynak var. Birincisi, oturum açmamış ziyaretçilere de AJAX ucu açan bir eklenti ve bu ucu tarayan botlar; bu istekler her seferinde tam bir WordPress yüklemesi maliyeti çıkarır. İkincisi, açık unutulmuş bir yönetim sekmesidir — dizüstü uykudan uyandığında nabız kaldığı yerden devam eder. IP kırılımına bakmak ikisini hemen ayırır.
Heartbeat'i kapatınca yazı kilidi ne olur?#
Yazı kilidi tamamen devre dışı kalır. WordPress "Bu yazı şu anda başka bir kullanıcı tarafından düzenleniyor" uyarısını Heartbeat üzerinden kurar; nabız yoksa uyarı da yoktur. İki editör aynı yazıyı açar, ikisi de kaydeder ve son kaydeden diğerinin çalışmasını siler. Kayıp, revizyon geçmişinden geri alınabilir ama fark edilene kadar genellikle saatler geçer.
Önbellek eklentisi kurarsam admin-ajax yükü azalır mı?#
Azalmaz. Sayfa önbellekleri POST isteklerini ve oturum açmış kullanıcıları atlar, dolayısıyla Heartbeat trafiği önbelleğin tamamen dışında kalır. İronik biçimde, ön yüzü iyi önbelleklenmiş bir sitede admin-ajax.php toplam PHP yükü içinde daha büyük bir paya sahip olur; çünkü diğer her şey azalmıştır. Bu yükü düşürmenin yolu önbellek değil, isteklerin kendisini seyrekleştirmektir.