Bali, Indonesia
Insights

Pindah ke PHP-FPM, Kenapa php_value di .htaccess Malah Bikin Error 500

October 28, 2026
0:00
Situs baru saja dipindah dari Apache mod_php ke PHP-FPM, lalu semua halaman langsung menampilkan 500 Internal Server Error. Kembali ke mod_php, situs normal lagi. Penyebabnya sering kali cuma beberapa baris kecil di .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.
Sekilas dulu soal perbedaannya. Dengan mod_php, PHP berjalan sebagai modul di dalam proses Apache sendiri. Dengan PHP-FPM (FastCGI Process Manager), PHP berjalan sebagai service terpisah dengan worker sendiri. Apache cuma mengoper request PHP ke service itu, biasanya lewat modul proxy FastCGI. Pola gejala kasus ini cukup khas. Error 500 muncul di semua URL dalam direktori yang punya .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.
Karena yang menolak request adalah Apache, jejaknya ada di error log Apache. Lokasinya tergantung distro dan control panel. Kalau ragu, cek directive 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/www
Pesan yang dicari kira-kira seperti ini (contoh format output, bukan dari insiden nyata):
TEXT
[...] [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 configuration
Pesan ini sudah menjawab hampir semuanya. Apache menyebut file mana yang bermasalah, directive apa yang tidak dikenal, dan alasannya: directive itu didefinisikan oleh modul yang tidak dimuat. Perintah grep di atas membantu menemukan baris serupa di subdirektori lain. Biasanya ada lebih dari satu .htaccess, misalnya di folder aplikasi lama atau di folder upload.
Setiap directive di konfigurasi Apache dimiliki oleh modul tertentu. 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.
Jebakan pertama adalah mengecek setting lewat terminal dengan 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.
Untuk pengaturan per situs atau per direktori, pengganti paling dekat adalah file .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
Ada tiga hal yang perlu diperhatikan. Pertama, tidak semua directive boleh ditaruh di .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.
Kalau kamu punya akses root dan setiap situs punya pool sendiri, setting juga bisa ditaruh di file pool PHP-FPM. Sintaksnya seperti contoh di blok sebelumnya: 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.
Langkah verifikasinya:
  1. Hapus atau beri komentar # pada semua baris php_* di .htaccess yang ditemukan oleh grep tadi.
  2. Buka ulang situs dan pastikan 500 hilang, termasuk untuk file statis.
  3. Buat file PHP sementara berisi pemanggilan phpinfo() di document root, lalu buka lewat browser.
  4. 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.ini atau pool sudah terbaca.
  5. Hapus file phpinfo() itu setelah selesai. File tersebut membocorkan banyak detail server kalau dibiarkan.
Kalau Local Value belum berubah, kemungkinan cache .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.