wp-cron itu cuma request HTTP ke situs sendiri
wp-cron.php di URL situs itu sendiri. Request dari server ke dirinya sendiri inilah yang disebut loopback request.
Request itu melewati jalur yang sama dengan pengunjung biasa. Nama domain di-resolve lewat DNS, koneksi dibuka ke port 443 kalau situs pakai HTTPS, terjadi TLS handshake, lalu baru PHP mengeksekusi wp-cron.php. Tes loopback di Site Health memakai jalur yang mirip. Jadi kalau salah satu langkah itu gagal, event tidak pernah dieksekusi. Penyebabnya bisa DNS yang mengarah ke IP yang salah, firewall yang memblokir IP server sendiri, basic auth, plugin keamanan, atau TLS.
Masalahnya, spawn cron dikirim secara non-blocking dengan timeout pendek. Pengunjung tidak melihat error apa pun, dan sering tidak ada jejak di log aplikasi. Dari luar kelihatannya cron diam saja, makanya cepat dicap rusak. Di support, keluhan seperti ini biasanya datang sebagai post tidak terbit, bukan sebagai masalah SSL. Akibatnya pengecekan pertama sering salah arah.
Kapan sertifikat benar-benar menggagalkan loopback
https_local_ssl_verify untuk menentukan apakah sertifikat diverifikasi, dan nilai default-nya false. Artinya, di instalasi standar tanpa modifikasi, sertifikat kedaluwarsa sering tidak menghentikan wp-cron. Cek sendiri di versi yang kamu pakai, karena detail seperti ini bisa berubah.
Sertifikat baru jadi penyebab ketika ada sesuatu di jalur loopback yang memverifikasinya. Kasus yang umum:
- Ada plugin, mu-plugin, atau kode di tema yang mengembalikan
truepada filterhttps_local_ssl_verify. Ini cukup sering dipasang sebagai bagian dari hardening. - Domain mengarah ke reverse proxy, CDN, atau load balancer yang memvalidasi sertifikat origin sebelum meneruskan request. Loopback dari server ikut lewat sana, sehingga ikut ditolak.
- Plugin lain, misalnya plugin backup atau page builder, memanggil REST API situs sendiri dengan verifikasi aktif. Hasilnya, gejalanya mirip tapi sumbernya berbeda.
cURL error 60 dengan keterangan soal sertifikat, arahnya jelas ke TLS. Kalau isinya timeout, 401, atau 403, kemungkinan besar penyebabnya bukan SSL. Arahkan pengecekan ke firewall, DNS, atau proteksi akses.
Kedaluwarsa atau chain tidak lengkap, cek dari server sendiri
notAfter sudah lewat. Chain tidak lengkap berarti server hanya mengirim sertifikat leaf (sertifikat milik domain) tanpa sertifikat intermediate dari CA. Akibatnya klien tidak bisa menyambungkan leaf itu ke root yang dipercaya.
Chain tidak lengkap sering membingungkan karena sebagian browser bisa melengkapi intermediate sendiri. Di browser gembok terlihat normal, tapi curl dan PHP di server gagal. Karena itu tes harus dijalankan lewat SSH dari server itu sendiri, bukan dari laptop. Jalur DNS, proxy, dan perilaku kliennya berbeda.
BASH
# 1) Lewat jalur yang sama dengan loopback (DNS domain apa adanya)
curl -sv -o /dev/null https://example.com/wp-cron.php 2>&1 | grep -Ei 'expire|issuer|SSL certificate|HTTP/'
# 2) Paksa langsung ke web server lokal, lewati DNS dan proxy
curl -sv -o /dev/null --resolve example.com:443:127.0.0.1 https://example.com/wp-cron.php 2>&1 | grep -Ei 'expire|issuer|SSL certificate|HTTP/'example.com dengan domainnya. Kalau web server tidak listen di 127.0.0.1, pakai IP server. Perbandingan dua hasil ini cukup informatif. Kalau tes pertama gagal tapi tes kedua lolos, masalahnya ada di lapisan depan, misalnya proxy atau CDN. Kalau keduanya gagal dengan pesan yang sama, masalahnya di sertifikat yang dipasang di server.
Pesan dengan pola certificate has expired mengarah ke sertifikat kedaluwarsa. Pesan unable to get local issuer certificate biasanya berarti chain tidak lengkap. Teks persisnya bisa berbeda tergantung TLS backend yang dipakai curl.
Untuk melihat isi sertifikat dan jumlah sertifikat yang dikirim server, pakai openssl s_client. Parameter -servername penting karena server dengan banyak domain memilih sertifikat berdasarkan SNI. Tanpa parameter itu, yang muncul bisa sertifikat default.
BASH
# Subject, issuer, dan tanggal berlaku sertifikat yang dikirim server
echo | openssl s_client -connect 127.0.0.1:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates
# Hitung jumlah sertifikat yang dikirim (leaf + intermediate)
echo | openssl s_client -connect 127.0.0.1:443 -servername example.com -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'notAfter sudah lewat, ada dua kemungkinan. Pertama, renewal memang gagal. Kedua, renewal sebenarnya berhasil tapi web server belum di-reload, sehingga masih menyajikan sertifikat lama. Kemungkinan kedua cukup sering terjadi. Bandingkan dengan tanggal di file sertifikat yang baru. Kalau hitungan sertifikat hanya 1, server cuma mengirim leaf. Konfigurasinya perlu diarahkan ke file full chain atau bundle intermediate. Nama dan lokasi file itu tergantung control panel dan ACME client yang dipakai.
Kenapa masalah ini makin sering muncul
Pola yang lebih aman di production
curl -k atau mematikan verifikasi lewat kode memang membuat error hilang dari Site Health. Tapi pengunjung, API eksternal, dan monitoring tetap melihat sertifikat yang bermasalah, dan kamu kehilangan sinyal paling awalnya. Cek log renewal di AutoSSL atau ACME client, pastikan konfigurasi web server menunjuk ke file full chain, lalu reload service. Setelah itu, ulangi dua tes di atas sampai keduanya bersih.
Setelah sertifikat beres, kurangi ketergantungan cron pada loopback. Set DISABLE_WP_CRON ke true di wp-config.php, lalu jalankan event lewat cron sistem dengan WP-CLI. Cara ini menjalankan PHP langsung tanpa request HTTP, sehingga tidak bergantung pada DNS, proxy, atau TLS. Jadwalnya juga tidak lagi bergantung pada ada atau tidaknya pengunjung.
BASH
# Cari kode yang memaksa verifikasi SSL pada loopback
grep -rn 'https_local_ssl_verify' wp-content/
# Tes spawn wp-cron dan lihat event yang menunggu
wp cron test
wp cron event list --fields=hook,next_run_relative
# Contoh baris crontab setelah DISABLE_WP_CRON aktif (interval hanya contoh)
# */5 * * * * cd /path/ke/wordpress && wp cron event run --due-now --quietwp cron test akan melaporkan bahwa WP-Cron dinonaktifkan setelah DISABLE_WP_CRON aktif. Itu normal. Untuk monitoring, opsi -checkend pada openssl x509 bisa dipakai di cron untuk memberi peringatan kalau sertifikat akan habis dalam jangka waktu tertentu. Contohnya -checkend 1209600 untuk 14 hari. Jalankan cek ini dari server, bukan hanya dari layanan monitoring luar, supaya chain yang tidak lengkap juga ikut tertangkap.
Jadi, kalau post terjadwal terlewat dan loopback gagal, baca dulu pesan error lengkapnya di Site Health. Lalu jalankan curl dari server sendiri dengan dan tanpa --resolve. Dua langkah itu biasanya cukup untuk memastikan apakah masalahnya di sertifikat, chain, atau lapisan lain. Pindah ke cron sistem membuat jadwal lebih andal, tapi bukan pengganti renewal yang sehat. Dengan masa berlaku yang terus memendek, renewal dan pengecekan chain sebaiknya diperlakukan sebagai proses rutin, bukan kejadian tahunan.