İki farklı sahne, aynı kök neden. Birincisi: bir yazıyı pazartesi sabah 09:00'a zamanlıyorsunuz, salı günü siteye girdiğinizde yazının hâlâ "Zamanlanmış" durumda beklediğini ya da listede kırmızı "Zamanlama kaçırıldı" uyarısıyla durduğunu görüyorsunuz. Yedekleme eklentiniz de üç gündür yeni bir arşiv üretmemiş.
İkincisi tam tersi: hosting firmanızdan "hesabınız entry process limitini sürekli aşıyor" uyarısı geliyor. cPanel'in Resource Usage grafiğine bakıyorsunuz, tepe noktaları sitenin en yoğun saatleriyle çakışıyor. Ham erişim kayıtlarını açtığınızda aynı satırın onlarca kez tekrarlandığını fark ediyorsunuz: POST /wp-cron.php.
Bu iki tablonun ortak nedeni, WordPress'in zamanlanmış görev sistemi olan WP-Cron'un aslında bir zamanlayıcı olmamasıdır. Aşağıda önce mekanizmanın gerçekte nasıl çalıştığını, sonra kuyruğu ölçmeyi, ardından WP-Cron'u kapatıp yerine gerçek bir sunucu cron'u kurmayı ve en önemlisi — kapattıktan sonra yedekleme ile e-posta görevlerinin durmadığını doğrulamayı ele alıyoruz.
WP-Cron Neden Gerçek Bir Cron Değil?#
Linux'taki cron daemon'u arka planda sürekli çalışır ve saat geldiğinde görevi tetikler; siteye kimsenin girmesi gerekmez. WordPress'in wp-cron.php dosyası ise böyle çalışmaz. WordPress her sayfa isteğinin sonunda veritabanındaki cron seçeneğine bakar, vakti gelmiş bir görev varsa kendi sitesine loopback adı verilen ikinci bir HTTP isteği atar ve görevleri o istek içinde çalıştırır.
Yani zamanlayıcının kalp atışı ziyaretçi trafiğidir. Bunun iki doğrudan sonucu vardır:
- Siteye kimse girmezse hiçbir görev çalışmaz. Saat 09:00'da yayınlanacak yazı, ilk ziyaretçi saat 14:20'de geldiğinde yayınlanır.
- Siteye çok kişi girerse her istekte bu kontrol tekrarlanır ve loopback istekleri birikir.
Görevlerin listesi wp_options tablosunda cron adlı tek bir satırda, serileştirilmiş bir dizi olarak durur. Kuyruğa hem çekirdek (wp_version_check, wp_scheduled_delete, publish_future_post) hem de eklentiler yazar: WooCommerce stok senkronizasyonu, yedekleme planları, e-posta kuyruğu, önbellek temizliği, SEO eklentisinin sitemap yenilemesi.
Bir noktayı baştan netleştirelim: WP-Cron'u kapatmak görevleri silmez. DISABLE_WP_CRON sabiti yalnızca "her istekte kontrol etme" davranışını durdurur. Kuyruk veritabanında olduğu gibi kalır, sadece onu tetikleyecek yeni bir mekanizma kurmanız gerekir. Bu yüzden kapatma ile cron kurma işlemleri aynı bakım penceresinde yapılmalıdır.
İki Farklı Belirti, Tek Kök Neden#
Sorunu tarif ederken hangi tarafta olduğunuzu bilmek, sonraki adımlarda seçeceğiniz aralığı belirler.
| Belirti | Site profili | Ne oluyor |
|---|---|---|
| Zamanlanmış yazı geç yayınlanıyor veya "kaçırıldı" diyor | Günde birkaç yüz ziyaret | Tetikleyecek istek yok, kuyruk bekliyor |
| Yedekleme günlerce çalışmıyor | Düşük trafik | Aynı sebep; yedek eklentisi zamanı gelmiş görevi göremiyor |
| Ürün stokları/abonelikler gecikiyor | Düşük-orta trafik | WooCommerce kuyruğu ilerlemiyor |
| CPU ve entry process limiti doluyor | Yüksek trafik | Her istekte loopback açılıyor |
| Site anlık olarak yanıt vermiyor | Yüksek trafik | Uzun süren bir görev isteği kilitliyor |
| Aynı görev iki kez çalışıyor | Karışık | Eş zamanlı istekler kuyruğu paralel tetikliyor |
Kaynak limiti tarafındaki belirtiler paylaşımlı hostingde daha görünürdür; hangi sayaçların dolduğunu ve nasıl okunacağını paylaşımlı hosting kaynak limitleri yazısında bulabilirsiniz. Her iki durumda da çözüm aynıdır: tetiklemeyi trafikten alıp sabit aralıklı bir zamanlayıcıya devretmek.
Önce Ölçün: Kuyrukta Ne Var?#
Kapatmadan önce ne olduğunu görmek, sonradan "acaba bir şeyi bozdum mu?" sorusunu tamamen ortadan kaldırır. İki yol var.
WP Crontrol eklentisi ile#
Eklentiyi kurup Araçlar → Cron Etkinlikleri ekranını açın. Karşınıza kayıtlı bütün kancalar (hook), sonraki çalışma zamanları ve tekrar aralıkları gelir. Burada dikkat edeceğiniz üç şey:
- Sonraki çalışma zamanı geçmişte kalmış görevler: kuyruk ilerlemiyor demektir.
- Aynı kancanın onlarca kez tekrar etmesi: bir eklenti görev üretip temizlemiyor.
- Silinmiş bir eklentiden kalan, artık karşılığı olmayan kancalar: bunlar her tetiklemede boşuna denenir.
WP-CLI ile#
SSH erişiminiz varsa daha hızlıdır:
cd /home/kullanici/public_html
# Kayıtlı bütün görevler ve ne zaman çalışacakları
wp cron event list
# Sadece vakti gelmiş olanlar
wp cron event list --fields=hook,next_run_relative --status=due
# Zamanlayıcının çalışıp çalışmadığını test et
wp cron test
wp cron test komutu loopback isteğinin başarılı olup olmadığını söyler; "WP-Cron spawning is working as expected" dışında bir çıktı alıyorsanız sorunun bir bölümü zaten loopback engelidir. WP-CLI'yi hiç kullanmadıysanız WP-CLI kullanımı yazısı kurulum ve temel komutları kapsıyor.
Bu listeyi bir kenara not edin. Gerçek cron'a geçtikten sonra aynı listeyi tekrar alıp karşılaştıracaksınız.
wp-config.php ile WP-Cron'u Kapatma#
Değişiklik tek satır. wp-config.php dosyasını açın ve şu satırı ekleyin:
define( 'DISABLE_WP_CRON', true );
Satırın yeri önemlidir: dosyanın sonundaki
/* That's all, stop editing! Happy publishing. */
yorumunun üstünde olmalıdır. O satırdan sonra WordPress wp-settings.php'yi yükler ve orada tanımlanan sabitler artık dikkate alınmaz. Türkçe kurulumlarda aynı yorum "Hepsi bu kadar, düzenlemeyi bırakın!" şeklinde görünür.
Düzenlemeye başlamadan wp-config.php'nin bir kopyasını alın; bu dosyadaki bir yazım hatası siteyi tamamen kapatır. Dosyanın diğer güvenlik ayarları için wp-config güvenlik yazısına bakabilirsiniz.
WP-CLI ile de ekleyebilirsiniz:
wp config set DISABLE_WP_CRON true --raw
wp config get DISABLE_WP_CRON
--raw bayrağı değeri tırnaksız, gerçek bir boolean olarak yazar. Tırnaklı 'true' metni de PHP'de doğru sayılır ama okurken kafa karıştırır.
Bu satırı ekledikten sonra sunucu tarafında cron kurmazsanız zamanlanmış görevleriniz tamamen durur. Sıradaki adımı aynı oturumda tamamlayın.
Gerçek Cron'u Kurma: cPanel, Plesk ve crontab#
Artık tetikleyiciyi siz sağlayacaksınız. Üç yöntem var; hangisinin size uyduğunu tablodan seçin.
Yöntem 1: HTTP isteği (paylaşımlı hosting için en uyumlu)#
cPanel'de Cron Jobs ekranını açın, "Common Settings" listesinden aralığı seçin veya beş alanı elle doldurun, komut alanına şunu yazın:
wget -q -O - "https://siteniz.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1
wget bulunmayan sistemlerde curl aynı işi görür:
curl -s "https://siteniz.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1
Bu yöntem WordPress'i tam olarak normal bir ziyaretçi gibi çağırır; $_SERVER bilgileri eksiksizdir, dolayısıyla site adresine bağımlı eklentiler sorunsuz çalışır. Dezavantajı, isteğin web sunucusu üzerinden geçmesi ve PHP'nin max_execution_time sınırına takılabilmesidir.
Yöntem 2: PHP CLI ile doğrudan çağırma#
/usr/local/bin/php /home/kullanici/public_html/wp-cron.php >/dev/null 2>&1
Web sunucusunu ve HTTP katmanını atladığı için daha hafiftir ve komut satırı PHP'sinin zaman aşımı genelde sınırsızdır. Buna karşılık istek bir HTTP isteği olmadığından HTTP_HOST gibi değerler bulunmaz; site adresini $_SERVER'dan okumaya çalışan eski eklentilerde uyarı üretebilir. PHP'nin tam yolunu which php ile ya da hosting panelinizin "PHP sürümü" ekranından doğrulayın — yanlış sürümü çağırmak sessiz hatalara yol açar.
Yöntem 3: WP-CLI ile (en temizi)#
/usr/local/bin/wp cron event run --due-now --path=/home/kullanici/public_html >/dev/null 2>&1
Yalnızca vakti gelmiş görevleri çalıştırır, hangi kancanın ne kadar sürdüğünü raporlayabilir ve HTTP katmanına hiç dokunmaz. SSH erişiminiz varsa tercih edilecek yöntem budur.
VDS/dedicated sunucuda crontab#
Kendi sunucunuzdaysanız görevi doğrudan site kullanıcısının crontab'ına yazın:
crontab -u www-data -e
Açılan dosyaya ekleyin:
MAILTO=""
*/5 * * * * /usr/local/bin/php /var/www/siteniz.com/wp-cron.php >/dev/null 2>&1
MAILTO="" satırı, cron'un her çalıştırmada size e-posta göndermesini engeller — beş dakikada bir gelen boş mesajlar kısa sürede posta kutunuzu doldurur. Crontab söz dizimi, özel dizeler ve PATH sorunları için Linux cron görevleri yazısı ayrıntılı referans sunuyor.
Görevi root yerine sitenin dosya sahibi kullanıcıyla çalıştırın; root ile üretilen önbellek ve yükleme dosyalarının sahipliği bozulur ve WordPress sonradan bu dosyalara yazamaz.
| Yöntem | Avantaj | Dikkat edilecek |
|---|---|---|
wget / curl | Her yerde çalışır, panel arayüzünden kurulur | PHP zaman aşımına takılabilir |
| PHP CLI | Web sunucusunu atlar, süre sınırı yok | $_SERVER eksik, PHP yolu doğru olmalı |
| WP-CLI | Sadece vakti geleni çalıştırır, raporlanabilir | SSH ve WP-CLI kurulumu gerekir |
Doğru Aralık Nasıl Seçilir?#
En sık yapılan yanlış, "ne kadar sık olursa o kadar iyi" varsayımıyla aralığı bir dakikaya çekmektir. Bu, kapatmak istediğiniz yükü geri getirir: her çalıştırma WordPress'i baştan yükler, eklentileri başlatır ve veritabanına bağlanır.
Belirleyici kural şudur: cron aralığınız, kuyruktaki en sık tekrarlanan görevden daha sık olmalıdır. WordPress'in yerleşik aralıkları hourly (saatlik), twicedaily (12 saatlik) ve daily'dir; eklentiler every_five_minutes gibi kendi aralıklarını da ekleyebilir. WP Crontrol'ün "Zamanlamalar" sekmesi sitenizde tanımlı bütün aralıkları listeler.
| Site tipi | Önerilen aralık | Gerekçe |
|---|---|---|
| Blog, kurumsal tanıtım sitesi | */15 * * * * | En sık görev genelde saatlik; 15 dakika fazlasıyla yeter |
| Düzenli yayın yapan içerik sitesi | */10 * * * * | Zamanlanmış yazılar en fazla 10 dakika gecikir |
| WooCommerce mağaza | */5 * * * * | Sipariş e-postaları, stok ve ödeme kontrolleri gecikmemeli |
| Abonelik, rezervasyon, kuyruklu e-posta | */1 * * * * veya */2 * * * * | Görevler dakikalık aralıklarla planlanıyorsa gerekir |
Zamanlanmış yazıların tam dakikasında yayınlanmasını bekliyorsanız beklentinizi düzeltin: cron aralığı ne ise, gecikme de en fazla o kadar olur. 15 dakikalık aralıkta 09:00'a zamanlanan yazı en geç 09:15'te çıkar.
Aralığı belirlerken şunu da hesaba katın: WordPress, eş zamanlı iki tetiklemenin aynı görevi çalıştırmasını WP_CRON_LOCK_TIMEOUT sabitiyle engeller ve varsayılan değeri 60 saniyedir. Yani bir dakikadan sık tetikleme yapmanın çoğu durumda pratik bir karşılığı yoktur.
Kapattıktan Sonra Yedek ve E-posta Görevlerinin Durmadığını Doğrulama#
Bu adım isteğe bağlı değil. Kapatma ile kurma arasında yapılan küçük bir hata (yanlış PHP yolu, yanlış dizin, HTTPS yerine HTTP) hiçbir hata mesajı üretmez — görevler sessizce durur ve bunu haftalar sonra, ihtiyaç duyduğunuz yedeği ararken fark edersiniz.
1. Cron'un tetiklendiğini kanıtlayın. Çıktıyı /dev/null'a atmak yerine geçici olarak bir dosyaya yazdırın:
*/5 * * * * /usr/local/bin/php /var/www/siteniz.com/wp-cron.php >> /var/log/wpcron.log 2>&1
Birkaç çalıştırma sonra dosyaya bakın:
tail -n 20 /var/log/wpcron.log
ls -l --time-style=full-iso /var/log/wpcron.log
Dosyanın değişim zamanı ilerliyorsa tetikleme çalışıyordur. Doğrulama bittikten sonra satırı >/dev/null 2>&1 hâline geri çevirin, aksi hâlde log dosyası zamanla diski doldurur.
2. Kuyruğun ilerlediğini kontrol edin. Kapatma öncesi aldığınız listeyle karşılaştırın:
wp cron event list --fields=hook,next_run_relative
Sonraki çalışma zamanları gelecekte görünüyorsa kuyruk akıyor demektir. Hâlâ geçmişte kalmış kancalar varsa cron ya çalışmıyor ya da o kancalar zaten hatalı.
3. Yedekleme görevini adıyla test edin. Yedekleme eklentinizin kancasını WP Crontrol'de bulup "Şimdi Çalıştır" bağlantısına tıklayın ya da WP-CLI ile tetikleyin:
wp cron event run updraft_backup_scheduled
Ardından eklentinin kendi arşiv listesine bakın; yeni bir kayıt oluştuysa görev sisteminiz sağlamdır. Yedekleme stratejisini gözden geçirmek isterseniz WordPress yedekleme yazısı planlama tarafını ele alıyor.
4. E-posta tarafını unutmayın. Bildirim, form ve sipariş e-postalarını kuyruğa alan eklentiler cron'a bağımlıdır. Kendinize test formu gönderin ve mesajın birkaç dakika içinde ulaştığını görün. Ulaşmıyorsa sorun cron'da değil gönderim ayarlarında da olabilir; WordPress SMTP e-posta yazısı o tarafı kapsıyor.
5. Bir hafta sonra tekrar bakın. Site Sağlığı ekranındaki uyarıları ve kuyruk listesini yeniden kontrol edin. Bu ekrandaki uyarıların anlamları için WordPress site sağlığı uyarıları yazısına başvurabilirsiniz.
Sık Karşılaşılan Sorunlar ve Çözümleri#
| Belirti | Sebep | Çözüm |
|---|---|---|
| Cron kurdum ama hiçbir görev çalışmıyor | Komuttaki yol veya PHP sürümü yanlış | Komutu SSH'ta elle çalıştırın; hata veriyorsa yol hatalıdır |
| Görevler iki kez çalışıyor | Hem DISABLE_WP_CRON eklenmemiş hem cron kurulmuş | wp config get DISABLE_WP_CRON ile sabiti doğrulayın |
wp cron test loopback hatası veriyor | Güvenlik duvarı veya WAF kendi sitesine isteği engelliyor | CLI tabanlı yöntem (2 veya 3) kullanın, HTTP yöntemini bırakın |
| Cron her çalıştırmada e-posta gönderiyor | MAILTO tanımlı değil | Crontab'ın başına MAILTO="" ekleyin |
| Kuyrukta artık var olmayan eklentinin kancaları var | Eklenti silinirken görevi temizlememiş | WP Crontrol'den ilgili kancayı silin |
| Bir görev çok uzun sürüyor, site yavaşlıyor | Ağır bir toplu işlem tek seferde çalışıyor | Aralığı seyreltin veya görevi gece saatlerine alın |
| Zamanlanmış yazı hâlâ "kaçırıldı" diyor | Sunucu ile WordPress saat dilimi uyuşmuyor | Ayarlar → Genel'den saat dilimini ve sunucu saatini karşılaştırın |
Loopback isteğinin engellendiği kurulumlarda karşınıza ALTERNATE_WP_CRON sabiti de çıkabilir. Bu sabit, ayrı bir loopback isteği açmak yerine ziyaretçiyi ?doing_wp_cron parametresi eklenmiş bir adrese yönlendirerek görevleri çalıştırır. Yükü azaltmaz, üstelik önbellek katmanlarıyla ve temiz URL yapısıyla sorun çıkarır. Gerçek cron kurabiliyorsanız bu sabite hiç ihtiyacınız olmaz; yalnızca sunucu cron'u kesinlikle kurulamayan ve loopback'in engellendiği kurulumlar için son çaredir.
Son bir not: bu değişiklik sitenin genel hızını doğrudan artırmaz, ziyaretçi başına düşen gereksiz PHP çalıştırmasını ortadan kaldırır. Sayfa yüklenme sürelerini iyileştirmek için önbellekleme ve görsel optimizasyonu gibi adımlarla birlikte ele alın; WordPress hız optimizasyonu yazısı bu adımların tam listesini içeriyor.
Sıkça Sorulan Sorular#
DISABLE_WP_CRON eklersem zamanlanmış görevlerim silinir mi?#
Hayır. Bu sabit yalnızca WordPress'in her sayfa isteğinde kuyruğu kontrol edip loopback isteği açma davranışını kapatır. Görev listesi wp_options tablosundaki cron kaydında olduğu gibi durur ve sabiti kaldırdığınız anda eski davranış geri gelir. Ancak sabit eklenmiş ve sunucu cron'u kurulmamışsa görevler tetiklenmediği için hiç çalışmaz.
Trafiği yüksek olmayan bir sitede de kapatmalı mıyım?#
Evet, hatta düşük trafikli sitelerde kapatmanın faydası daha büyüktür. Yüksek trafikte sorun gereksiz yük, düşük trafikte ise görevlerin hiç çalışmamasıdır. Günde birkaç ziyaretçi alan bir sitede zamanlanmış yazılar günler sonra yayınlanabilir, yedekler hiç alınmayabilir. Sabit aralıklı gerçek cron her iki durumu da çözer.
cPanel'de cron ekleyemiyorum, alternatifim var mı?#
Bazı paylaşımlı paketlerde cron menüsü kapalı olabilir. Bu durumda ilk seçenek hosting firmasından görevi sizin adınıza tanımlamasını istemektir. Mümkün değilse harici bir "cron tetikleyici" servis kullanabilir, belirlediğiniz aralıkla wp-cron.php adresinize istek attırabilirsiniz. Bu yöntemde adres herkese açık olduğundan aralığı makul tutun ve sunucu tarafındaki bir cron kadar güvenilir olmadığını bilin.
Zamanlanmış yazım neden "kaçırıldı" uyarısı veriyor?#
Bu uyarı, publish_future_post kancasının planlanan saatte tetiklenmediğini gösterir. En yaygın sebep tetikleyici eksikliğidir, ama saat dilimi uyuşmazlığı da aynı sonucu üretir: WordPress ayarlarındaki saat dilimi ile sunucunun saati farklıysa görev yanlış zamana yazılır. Gerçek cron kurduktan sonra sorun sürüyorsa Ayarlar ekranından saat dilimini kontrol edin.
Cron aralığını bir dakikaya çekersem sorun olur mu?#
Çoğu site için gereksizdir ve kapatmak istediğiniz yükün bir bölümünü geri getirir; her çalıştırma WordPress'i baştan yükler. Ayrıca WordPress varsayılan olarak 60 saniyelik bir kilit süresi uygular, bu yüzden dakikadan sık tetiklemenin pratik karşılığı yoktur. Görevleriniz gerçekten dakikalık aralıklarla planlanmıyorsa 5 ile 15 dakika arası bir değer daha uygundur.
WP-Cron kapalıyken bir görevi elle çalıştırabilir miyim?#
Evet. WP Crontrol eklentisinde her kancanın yanındaki "Şimdi Çalıştır" bağlantısı görevi anında tetikler. SSH erişiminiz varsa wp cron event run <kanca-adi> komutu aynı işi yapar, wp cron event run --due-now ise vakti gelmiş bütün görevleri çalıştırır. Bu yöntemler kurulum sonrası doğrulama yaparken de en pratik test aracıdır.