Bali, Indonesia
Insights

Cara Cek Owner dan Permission File WordPress Setelah Pindah dari cPanel ke VPS

October 29, 2026
0:00
Situs WordPress yang baru dipindah dari cPanel ke VPS sering tampil normal, tapi upload gambar gagal, update plugin tiba-tiba minta data FTP, atau muncul pesan permission denied di log. Jalan pintas yang sering diambil adalah chmod -R 777 supaya semuanya bisa ditulis. Website memang jalan lagi. Masalahnya, sekarang semua file bisa diubah oleh proses apa pun di server, termasuk script yang masuk lewat plugin bermasalah. Di sini kita bahas cara mengecek owner dan permission yang benar, lalu memperbaikinya tanpa membuat update plugin dari dashboard berhenti berfungsi.
Di server cPanel, setiap akun hosting punya user Linux sendiri. File situs dimiliki user itu. Pada banyak konfigurasi, PHP juga dijalankan sebagai user yang sama, misalnya lewat PHP-FPM per akun atau handler seperti suPHP dan LSAPI. Karena PHP dan file sama-sama milik satu user, WordPress bisa menulis ke folder sendiri tanpa trik tambahan. Saat dipindah ke VPS, model ini biasanya tidak ikut terbawa. Polanya kira-kira seperti ini:
  • rsync -a yang dijalankan sebagai root menyimpan UID (nomor user) dari server asal. Kalau user dengan UID itu tidak ada di VPS, ls -l hanya menampilkan angka, bukan nama.
  • Arsip .tar.gz yang diekstrak sebagai root biasanya mempertahankan owner asli. Arsip .zip yang diekstrak sebagai root umumnya menghasilkan file milik root.
  • File yang di-upload lewat SFTP dengan login root otomatis jadi milik root.
Sementara itu, PHP-FPM di VPS sering berjalan sebagai user lain, misalnya www-data atau user yang kamu buat sendiri. Hasilnya, PHP masih bisa membaca file karena permission 644 mengizinkan siapa pun membaca. Tapi PHP tidak bisa menulis, sehingga upload dan update gagal. Sedikit pengingat soal angka permission. Tiga digit itu berlaku untuk owner, group, dan others (semua user lain). Nilai r=4, w=2, x=1 dijumlahkan, jadi 755 artinya owner bisa baca, tulis, dan masuk folder, sedangkan yang lain hanya baca dan masuk. 777 artinya semua user bisa melakukan semuanya. Sebagian handler seperti suPHP bahkan menolak menjalankan file yang group-writable atau world-writable, tergantung setting-nya. Jadi 777 tidak selalu membuat situs jalan.
Sebelum mengubah apa pun, kita perlu tahu target owner-nya. Aturan dasarnya: kalau kamu ingin update plugin dan upload media bekerja dari dashboard, owner file harus sama dengan user yang menjalankan proses PHP. Bukan user web server, dan bukan root.
BASH
# User yang menjalankan worker PHP-FPM (kolom pertama)
ps -eo user,group,args | grep '[p]hp-fpm'

# User yang diset di konfigurasi pool
# Path ini untuk Debian/Ubuntu. Keluarga RHEL biasanya di /etc/php-fpm.d/
grep -RE '^(user|group|listen\.owner|listen\.group)\s*=' /etc/php/*/fpm/pool.d/

# User web server, untuk perbandingan
ps -eo user,args | grep -E '[n]ginx|[a]pache2|[h]ttpd|[l]itespeed'
Biasanya ada satu proses master yang berjalan sebagai root, ditambah beberapa worker. Yang penting adalah user di baris worker, karena proses itulah yang mengeksekusi kode WordPress. Kalau kamu memakai LiteSpeed atau OpenLiteSpeed dengan lsphp, user PHP ditentukan di konfigurasi virtual host atau suEXEC, bukan di pool PHP-FPM. Cara mengeceknya tergantung setup. Ada satu tanda cepat yang sering membingungkan pemilik situs: dashboard menampilkan form Connection Information yang meminta host FTP saat update plugin. Ini hampir selalu soal owner. Saat update, WordPress membuat file sementara, lalu membandingkan owner file itu dengan owner file WordPress. Kalau berbeda, WordPress tidak memakai metode tulis langsung dan meminta kredensial FTP atau SSH. Menambahkan define('FS_METHOD', 'direct'); di wp-config.php memang menghilangkan form itu, tapi akar masalahnya tetap ada. File baru akan dimiliki user PHP, sedangkan file lama tetap milik root, sehingga kepemilikannya jadi campur aduk.
Setelah tahu user PHP-nya, misalnya siteuser, kita lihat kondisi file saat ini. Semua perintah di bawah hanya membaca, jadi aman dijalankan kapan saja. Variabel WP sengaja ditulis tanpa tanda kutip. Kalau path kamu mengandung spasi, bungkus $WP dengan tanda kutip ganda.
BASH
WP=/var/www/example.com/public_html

# Owner, group, permission oktal, dan nama untuk titik-titik penting
stat -c '%U:%G %a %n' WPWP/wp-config.php WP/wp-contentWP/wp-content/uploads

# File dengan UID/GID yatim (sisa dari server lama)
find $WP \( -nouser -o -nogroup \) | head

# File atau folder yang owner-nya bukan user PHP
find $WP ! -user siteuser | wc -l

# Apa pun yang bisa ditulis others (sisa chmod 777), symlink dikecualikan
find $WP ! -type l -perm -o+w | wc -l

# contoh format output stat (bukan dari server nyata):
# root:root 777 /var/www/example.com/public_html
# root:root 777 /var/www/example.com/public_html/wp-config.php
Cara membacanya cukup sederhana. Kalau stat menampilkan root:root atau angka seperti 1005:1005, owner belum sesuai. Kalau perintah find ! -user mengembalikan angka selain nol, masih ada file yang bukan milik user PHP. Perintah -perm -o+w mencari bit tulis untuk others. Pada situs WordPress yang sehat, hasilnya seharusnya nol. Symlink sengaja dikecualikan karena di Linux symlink selalu tampil dengan permission 777. Itu normal dan tidak berarti apa-apa.
Ada dua model yang umum dipakai, dan pilihannya tergantung cara kamu mengelola situs. Model pertama meniru cPanel. Semua file dimiliki user PHP, misalnya siteuser, dan PHP-FPM untuk situs itu berjalan sebagai siteuser. Update dari dashboard berjalan normal. Kekurangannya, kalau ada plugin yang bisa dieksploitasi, penyerang juga bisa mengubah file core dan plugin karena PHP punya hak tulis di semua tempat. Model kedua lebih ketat. File dimiliki user deploy, sedangkan PHP hanya diberi hak tulis di wp-content/uploads dan folder cache. Model ini lebih aman, tapi update plugin dari dashboard tidak akan berjalan. Update harus dilakukan lewat WP-CLI sebagai user deploy atau lewat pipeline deploy. Untuk sebagian besar VPS yang dikelola sendiri dan masih mengandalkan update dari dashboard, model pertama adalah titik awal yang realistis. Targetnya kira-kira seperti ini:
BagianOwner:groupPermissionCatatan
Semua foldersiteuser:siteuser755Bit x pada folder diperlukan agar bisa dimasuki
Semua filesiteuser:siteuser644Web server tetap bisa membaca file statis
wp-config.phpsiteuser:siteuser640 atau 600Berisi password database, others tidak perlu membaca
wp-content/uploadssiteuser:siteuser755Cukup karena PHP adalah owner, tidak perlu 775 atau 777
640 aman untuk wp-config.php di model ini karena file itu dibaca oleh PHP sebagai owner. Nginx tidak perlu membacanya karena Nginx hanya meneruskan request PHP ke PHP-FPM. Kalau PHP ternyata berjalan sebagai user lain, permission ini akan membuat situs langsung error. Karena itu, langkah identifikasi user PHP tadi tidak boleh dilewati.
Perintah di bawah mengubah owner dan permission secara rekursif. Ini berisiko. Kalau WP menunjuk ke path yang salah, misalnya /var/www atau /, owner file sistem bisa ikut berubah dan kondisinya sulit dikembalikan. Sebelum menjalankan perintah ini:
  1. Pastikan echo $WP menampilkan folder situs yang benar.
  2. Simpan dulu permission lama dengan getfacl sebagai titik balik.
  3. Jalankan perubahan.
BASH
WP=/var/www/example.com/public_html
echo $WP

# 1. Simpan permission lama. Pulihkan dengan: setfacl --restore=/root/wp-perms-backup.acl
getfacl -R -p $WP > /root/wp-perms-backup.acl

# 2. Kembalikan owner ke user PHP
chown -R siteuser:siteuser $WP

# 3. Folder 755, file 644, wp-config.php 640
find $WP -type d -exec chmod 755 {} +
find $WP -type f -exec chmod 644 {} +
chmod 640 $WP/wp-config.php
find ... -exec ... {} + memisahkan folder dan file. Cara ini lebih aman daripada chmod -R 755, yang akan memberi bit execute ke semua file. Secara default, chown -R juga tidak menelusuri symlink, sehingga folder di luar situs tidak ikut terubah. Setelah itu, verifikasi ulang dari sisi PHP, jangan dari sesi root:
  • Jalankan lagi dua perintah find dari bagian audit. find $WP ! -user siteuser | wc -l dan pencarian -perm -o+w seharusnya sama-sama menghasilkan nol.
  • Uji hak tulis sebagai user PHP dengan sudo -u siteuser touch $WP/wp-content/uploads/.tes-tulis, lalu hapus file itu. Kalau perintahnya gagal, berarti masih ada folder induk yang owner-nya keliru.
  • Jalankan sudo -u siteuser wp plugin list --path=$WP untuk memastikan WP-CLI bisa membaca instalasi sebagai user yang benar. Kalau WP-CLI belum terbiasa kamu pakai untuk troubleshooting, ada catatan di Cara Cek Plugin Penyebab White Screen WordPress Lewat WP-CLI Tanpa Buka Admin.
  • Coba update satu plugin kecil dari dashboard. Form permintaan FTP seharusnya tidak muncul lagi.
Kalau situs sempat berjalan dengan 777, jangan anggap masalahnya selesai setelah permission dirapikan. Selama 777 aktif, siapa pun yang bisa menjalankan proses di server punya hak tulis ke semua file. Minimal, cek file PHP yang seharusnya tidak ada di folder upload dengan find $WP/wp-content/uploads -name '*.php'. Kalau ada yang mencurigakan, langkah pembersihannya lebih panjang dari sekadar ganti password, seperti dibahas di Mengira Ganti Password Admin Cukup Setelah WordPress Dibobol. Pegangan praktisnya: setiap selesai migrasi, cari tahu dulu user yang menjalankan PHP, lalu samakan owner file dengan user itu. Pakai 755 untuk folder, 644 untuk file, dan 640 untuk wp-config.php. Setelah itu, jalankan audit find yang sama sampai hasilnya nol. Kalau dashboard meminta FTP saat update, periksa owner file, bukan permission-nya, dan jangan menjadikan chmod 777 atau FS_METHOD sebagai solusi permanen.