.htaccess, misalnya php_value memory_limit 256M atau php_flag display_errors off. Baris itu dulu aman, sekarang membuat Apache menolak request bahkan sebelum PHP sempat jalan.
Kita telusuri dari gejala sampai akar masalahnya. Setelah itu kita pindahkan setting-nya ke tempat yang memang dibaca PHP-FPM.
Gejalanya: 500 di semua halaman, tapi log PHP kosong
.htaccess bermasalah. Coba buka file statis seperti gambar atau robots.txt di direktori yang sama. Kalau file statis pun ikut 500, hampir pasti masalahnya ada di level konfigurasi Apache, bukan di kode PHP. File statis sama sekali tidak menyentuh PHP, tapi .htaccess tetap dibaca Apache untuk setiap request di direktori itu.
Petunjuk kedua: log error PHP dan debug.log WordPress kosong, walaupun WP_DEBUG sudah dinyalakan. Ini wajar karena PHP memang tidak pernah dipanggil. Di meja support, ini salah satu hal yang paling sering membingungkan. Pemilik situs fokus mencari plugin yang rusak, padahal request-nya gagal satu lapis lebih awal.
Baca error log Apache, bukan log PHP
ErrorLog di konfigurasi virtual host situs tersebut.
BASH
# Contoh Debian/Ubuntu; di keluarga RHEL biasanya /var/log/httpd/error_log
sudo tail -n 50 /var/log/apache2/error.log
# Cari semua baris php_* di .htaccess (read-only, ganti path sesuai document root)
grep -rn --include='.htaccess' -E '^\s*php_(value|flag|admin_value|admin_flag)' /var/wwwTEXT
[...] [core:alert] [pid 1234] [client 203.0.113.10:51234] /var/www/example/public_html/.htaccess: Invalid command 'php_value', perhaps misspelled or defined by a module not included in the server configurationgrep di atas membantu menemukan baris serupa di subdirektori lain. Biasanya ada lebih dari satu .htaccess, misalnya di folder aplikasi lama atau di folder upload.
Kenapa php_value hanya dimengerti mod_php
RewriteRule milik mod_rewrite, Header milik mod_headers, dan php_value serta php_flag milik mod_php. Saat mod_php dimuat, modul itu mendaftarkan directive tersebut ke Apache dan meneruskan nilainya ke PHP yang berjalan di proses yang sama.
Begitu pindah ke PHP-FPM, mod_php biasanya dinonaktifkan. Selain tidak dibutuhkan lagi, mod_php juga umumnya tidak cocok dipakai bersama MPM event atau worker yang sering dipasangkan dengan PHP-FPM. Akibatnya tidak ada lagi modul yang mengenali php_value. Apache tidak mengabaikan directive yang tidak dikenal. Dia memperlakukannya sebagai kesalahan konfigurasi dan menghentikan request dengan 500.
PHP-FPM sendiri juga tidak bisa membantu. Dia tidak membaca .htaccess sama sekali, karena file itu murni urusan Apache. Jadi meskipun mod_php masih aktif, setting di .htaccess tidak akan pernah sampai ke worker PHP-FPM. Hal yang sama berlaku kalau situs dipindah ke Nginx: Nginx tidak membaca .htaccess, sehingga baris itu diam saja dan setting-nya hilang tanpa error.
Dua jebakan yang sering bikin salah arah
php -i atau php -r. Perintah itu menjalankan PHP CLI, yang biasanya memakai php.ini sendiri, terpisah dari PHP-FPM. Hasilnya bisa menunjukkan memory_limit sudah benar, padahal worker FPM yang melayani website memakai nilai lain. Untuk melihat apa yang benar-benar dipakai website, cek dari sisi web (caranya ada di bagian verifikasi di bawah).
Jebakan kedua adalah "memperbaiki" dengan membungkus baris tersebut dalam blok IfModule, misalnya IfModule mod_php7.c. Error 500 memang hilang karena Apache melewati isi blok itu saat modulnya tidak ada. Tapi setting-nya juga ikut hilang tanpa pemberitahuan. Situs kembali normal, lalu beberapa hari kemudian upload file besar gagal karena upload_max_filesize kembali ke default. Pembungkus IfModule boleh dipakai sebagai pengaman sementara, asalkan setting-nya juga dipindah ke tempat yang benar.
Perlu dicatat juga, beberapa control panel menulis blok php_value sendiri ke .htaccess lengkap dengan komentar penanda. Kalau kamu menemukan blok seperti itu, ubah setting-nya lewat fitur panel, bukan dengan edit manual, supaya tidak ditimpa lagi. LiteSpeed juga punya perilaku sendiri terhadap directive ini. Kalau server kamu LiteSpeed, cek dokumentasinya dulu sebelum menyimpulkan.
Memindahkan setting ke .user.ini
.user.ini. PHP yang berjalan lewat CGI atau FastCGI, termasuk PHP-FPM, membaca file ini. Pencariannya dimulai dari direktori script yang dieksekusi, lalu naik sampai document root. Formatnya INI biasa, bukan sintaks php_value. Jadi php_value memory_limit 256M berubah menjadi memory_limit = 256M, dan php_flag display_errors off menjadi display_errors = Off.
INI
; File .user.ini di document root (format INI, bukan php_value)
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
display_errors = Off
; Setara di file pool PHP-FPM, misalnya pool.d/namasitus.conf
; php_value[memory_limit] = 256M
; php_admin_value[upload_max_filesize] = 64M
; php_admin_flag[display_errors] = off.user.ini. Hanya directive dengan mode PHP_INI_PERDIR atau PHP_INI_USER yang berlaku. Directive tingkat sistem seperti disable_functions akan diabaikan. Cek mode tiap directive di dokumentasi PHP kalau ragu.
Kedua, PHP menyimpan cache isi .user.ini selama user_ini.cache_ttl detik. Default-nya 300 detik, jadi perubahan bisa baru terasa beberapa menit kemudian. Kalau butuh langsung, reload service PHP-FPM.
Ketiga, pastikan .user.ini tidak bisa dibuka dari browser. Aturan bawaan Apache biasanya hanya memblokir file berawalan .ht. Coba buka URL file itu langsung. Kalau isinya tampil, tambahkan aturan Files di konfigurasi Apache yang menolak akses ke .user.ini.
Atau ke konfigurasi pool PHP-FPM
php_value[nama] = nilai dan php_flag[nama] = on/off. Untuk yang tidak boleh diubah oleh aplikasi, gunakan versi php_admin_value dan php_admin_flag. Nilai dari versi admin tidak bisa ditimpa oleh .user.ini maupun ini_set() di kode.
Trade-off-nya jelas. Pool berlaku untuk semua situs yang memakai pool itu, dan perubahannya butuh reload service. Sebaliknya, .user.ini bisa diatur pemilik situs sendiri tanpa akses root. Sebagai rule of thumb, batas yang sifatnya kebijakan server lebih cocok di pool dengan php_admin_*. Kebutuhan spesifik aplikasi, seperti batas upload untuk satu situs, cukup di .user.ini.
Lokasi file pool tergantung distro dan panel. Di Debian/Ubuntu biasanya ada di bawah /etc/php/<versi>/fpm/pool.d/. Panel seperti cPanel umumnya meregenerasi file pool, jadi ubah lewat antarmuka atau mekanisme yang disediakan panel. Sebelum reload, jalankan binary php-fpm dengan opsi -t untuk mengetes sintaks konfigurasi. Nama binary-nya juga beda per distro, misalnya php-fpm8.2 di Debian/Ubuntu. Kalau situs ramai dan kamu sekalian menyetel jumlah worker di pool, pembahasan soal itu ada di Unraveling PHP-FPM Pool Starvation in High-Concurrency WordPress Hosting.
Verifikasi dari sisi web
- Hapus atau beri komentar
#pada semua barisphp_*di.htaccessyang ditemukan olehgreptadi. - Buka ulang situs dan pastikan 500 hilang, termasuk untuk file statis.
- Buat file PHP sementara berisi pemanggilan
phpinfo()di document root, lalu buka lewat browser. - Pastikan baris Server API menunjukkan FPM/FastCGI. Kolom Local Value harus menampilkan nilai baru untuk
memory_limit,upload_max_filesize, dan setting lain yang dipindah. Kalau Local Value berbeda dari Master Value, artinya.user.iniatau pool sudah terbaca. - Hapus file
phpinfo()itu setelah selesai. File tersebut membocorkan banyak detail server kalau dibiarkan.
.user.ini belum kedaluwarsa, file-nya ada di direktori yang salah, atau directive-nya memang tidak boleh diatur per direktori.
Intinya, setiap kali pindah dari mod_php ke PHP-FPM, jadikan pencarian php_value dan php_flag di semua .htaccess sebagai bagian dari checklist migrasi, bukan hal yang baru dicari setelah situs error. Pindahkan nilainya ke .user.ini atau pool, verifikasi lewat phpinfo() dari browser, dan jangan puas hanya karena 500-nya hilang setelah dibungkus IfModule.