D
P
0
← Semua artikel Read in English

Cache & CDN: Deploy Sukses, Perubahan Tak Muncul

Login Balas `302` ke /account/ tapi Browser Tetap Logged-Out: Cloudflare Flexible SSL Bikin `is_ssl()` False di Origin

· · 4 menit baca
Login Balas `302` ke /account/ tapi Browser Tetap Logged-Out: Cloudflare Flexible SSL Bikin `is_ssl()` False di Origin

Bug paling melelahkan bukan yang errornya galak, tapi yang servernya bilang "sukses" sambil hasilnya nol. Di sebuah situs membership klien, form login-nya bekerja sempurna menurut semua log yang saya punya. Menurut browser, tidak ada yang pernah terjadi.

Gejalanya: server bilang sukses, browser bilang siapa kamu

Alurnya standar. Form login POST ke admin-post.php, handler-nya memverifikasi kredensial, lalu redirect ke /account/.

Yang saya lihat di network tab:

POST /wp-admin/admin-post.php   302
Location: /account/
cf-cache-status: DYNAMIC

Semuanya benar. Statusnya 302, bukan 200 dengan pesan error. Tujuan redirect-nya tepat. cf-cache-status: DYNAMIC memastikan respons itu bukan halaman basi dari edge Cloudflare, jadi teori "ini cuma cache" langsung mati di awal.

Lalu browser mengikuti redirect ke /account/, dan /account/ melempar balik ke halaman login. Buka /wp-admin/ manual, sama saja, bounce ke login. Kredensial benar, sesi sisi server terbentuk, tapi dari sudut pandang request berikutnya saya tetap tamu.

Cookie auth-nya tidak pernah nempel.

Jebakan pertama: dua penyakit menyamar jadi satu gejala

Sebelum masuk ke penyebab sebenarnya, ada satu jam yang saya buang karena percaya semua kegagalan berasal dari satu sumber.

Situs itu punya throttle login buatan sendiri, dan throttle itu sudah lebih dulu mengunci IP mesin tester kami gara-gara puluhan percobaan login berulang selama debugging. Jadi sebagian percobaan gagal karena cookie tidak nempel, dan sebagian lagi gagal karena kami memang sedang diblokir. Dua sebab berbeda, satu tampilan yang sama persis: kembali ke halaman login.

Selama dua sebab itu masih bercampur, tiap eksperimen jadi tidak bisa dibaca. Saya ubah satu hal, hasilnya membaik, lalu memburuk lagi tanpa alasan, padahal yang berubah cuma apakah throttle sedang aktif atau tidak.

Yang akhirnya membereskan kebuntuan itu bukan ide pintar, tapi disiplin membosankan: matikan throttle-nya sementara, lalu tes dari jaringan yang benar-benar bersih. Setelah itu gejalanya jadi konsisten, dan bug yang konsisten baru layak di-debug.

Akar masalahnya: origin tidak tahu kalau pengunjung datang lewat HTTPS

Setelah gejalanya stabil, saya cek satu hal yang selama ini saya anggap sudah pasti benar. Apa yang dilihat PHP soal protokol request?

add_action('init', function () {
    error_log(sprintf(
        'is_ssl=%s | XFP=%s | HTTPS=%s',
        var_export(is_ssl(), true),
        $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? 'none',
        $_SERVER['HTTPS'] ?? 'unset'
    ));
});

Isinya bikin semua potongan langsung jatuh ke tempatnya:

is_ssl=false | XFP=https | HTTPS=unset

Pengunjung membuka situs lewat HTTPS. Cloudflare menerima koneksi terenkripsi itu, lalu meneruskannya ke origin sebagai HTTP polos. Itu mode Flexible SSL. Dari sisi pengunjung gemboknya hijau, tapi dari sisi origin request itu terlihat seperti trafik HTTP biasa. $_SERVER['HTTPS'] tidak pernah di-set, jadi is_ssl() mengembalikan false.

Dan is_ssl() bukan fungsi sepele di WordPress. Dia ikut menentukan bagaimana cookie autentikasi dipasang, termasuk apakah cookie itu ditandai aman untuk konteks HTTPS. Begitu origin salah menilai protokolnya, keputusan soal cookie diambil untuk dunia yang berbeda dari dunia tempat browser sebenarnya berada. Hasilnya cookie yang tidak cocok dengan konteks yang memintanya, dan setiap request berikutnya kembali dianggap belum login.

Makanya semuanya terlihat sukses. Verifikasi password memang lolos. Redirect memang dikirim. Yang tidak pernah terjadi cuma satu langkah paling akhir, dan langkah itu tidak muncul di log mana pun.

Perbaikannya: kembalikan kebenaran protokol ke origin

Cloudflare tetap mengirim informasi aslinya lewat header X-Forwarded-Proto. Origin cuma perlu diajari membacanya, dan pengajaran itu harus terjadi sebelum WordPress mulai mengambil keputusan apa pun.

Taruh di wp-config.php, di atas baris yang me-require wp-settings.php:

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
    && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}
 
/* That's all, stop editing! */
require_once ABSPATH . 'wp-settings.php';

Posisinya penting. Kalau kamu menaruh ini di functions.php tema atau di plugin, sebagian keputusan sudah diambil sebelum kodenya sempat jalan. Ini harus di paling awal bootstrap, saat belum ada apa pun yang membaca is_ssl().

Setelah itu, cek ulang lognya. is_ssl=true, cookie nempel, /account/ berhenti melempar balik.

Perlu dicatat, ini penambal yang jujur soal statusnya. Dia membuat PHP melihat protokol yang benar, tapi lalu lintas Cloudflare ke origin tetap HTTP polos. Kalau nanti origin-nya bisa melayani HTTPS sendiri dengan sertifikat yang valid, mode SSL-nya yang seharusnya dinaikkan, dan shim ini turun jadi jaring pengaman saja.

Satu langkah yang jangan dilewat: bypass cache untuk path yang butuh login

Ada satu hal lagi yang wajib dikunci sebelum menyatakan selesai. Halaman yang isinya berbeda per user tidak boleh pernah dilayani dari cache edge. Kalau /account/ atau area admin sempat masuk cache, satu pengunjung bisa melihat halaman milik pengunjung lain, dan itu kelas bug yang jauh lebih mahal daripada login yang gagal.

Jadi buat Cache Rule dengan aksi bypass untuk path yang terautentikasi, dan jangan andalkan asumsi bahwa "WordPress kan dinamis, pasti tidak di-cache". Buktikan lewat header:

curl -sI https://situs-anda.com/account/ | grep -i "cf-cache-status\|cache-control"

Yang kamu mau lihat di sana adalah BYPASS atau DYNAMIC, bukan HIT.

Yang saya bawa pulang