When WordPress asks for FTP or SFTP credentials to install plugins, update themes, or perform core updates, it means the PHP process cannot write to the WordPress files directly. This is almost always a file ownership mismatch between the PHP-FPM pool user and the owner of the WordPress files on disk. This guide explains the root cause and how to fix it properly.
Why WordPress Asks for FTP Credentials
Before writing to the filesystem (installing a plugin, updating a theme, etc.), WordPress calls get_filesystem_method() to determine how it should write files. This function creates a temporary file in the WordPress directory and checks if the owner of that temp file matches the owner of the WordPress files.
- If the owners match, WordPress uses the direct method — it writes files directly using PHP's file functions.
- If the owners do not match, WordPress assumes it does not have proper permissions and falls back to requesting FTP/SFTP credentials to write files as a different user.
The temp file is created by PHP, so its owner is whatever user PHP-FPM is running as. If PHP-FPM runs as www-data but the WordPress files are owned by ubuntu (or any other user), the owners will not match and you will see the FTP prompt.
Understanding PHP-FPM Pool Configuration
PHP-FPM (FastCGI Process Manager) runs PHP as separate worker processes. Each "pool" defines a set of worker processes with specific settings, including the system user and group they run as. Pool configurations are stored in:
# Default pool file:
/etc/php/8.2/fpm/pool.d/www.conf
# Or for other PHP versions:
/etc/php/7.4/fpm/pool.d/www.conf
/etc/php/8.0/fpm/pool.d/www.conf
/etc/php/8.1/fpm/pool.d/www.conf
Key Settings in the Pool Config
[www]
; The user and group PHP-FPM workers run as:
user = www-data
group = www-data
; The socket or port to listen on:
listen = /run/php/php8.2-fpm.sock
; Socket ownership:
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
The Fix: Match PHP-FPM User to File Owner
You have two options: change the PHP-FPM pool user to match the file owner, or change the file ownership to match the PHP-FPM user.
Option 1: Change the PHP-FPM Pool User (Recommended for Multi-site Servers)
If your WordPress files are owned by a specific user (e.g., wpuser), create a custom pool that runs as that user:
# Create a custom pool file:
sudo nano /etc/php/8.2/fpm/pool.d/wordpress.conf
[wordpress]
user = wpuser
group = wpuser
listen = /run/php/php8.2-fpm-wordpress.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
php_admin_value[open_basedir] = /var/www/wordpress/:/tmp/
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
Then update your Nginx or Apache configuration to use the new socket:
# Nginx:
fastcgi_pass unix:/run/php/php8.2-fpm-wordpress.sock;
# Apache (with mod_proxy_fcgi):
SetHandler "proxy:unix:/run/php/php8.2-fpm-wordpress.sock|fcgi://localhost"
Option 2: Change File Ownership to Match PHP-FPM User
If PHP-FPM runs as www-data, change the WordPress file ownership:
# Change ownership of all WordPress files:
sudo chown -R www-data:www-data /var/www/wordpress/
# Set correct directory permissions:
sudo find /var/www/wordpress/ -type d -exec chmod 755 {} \;
# Set correct file permissions:
sudo find /var/www/wordpress/ -type f -exec chmod 644 {} \;
This is simpler but means all WordPress files are owned by the web server user, which has security implications on shared hosting.
Setting Correct Directory and File Permissions
Regardless of which approach you use, the permissions should be:
| Path | Permissions | Notes |
|---|---|---|
| Directories | 755 (rwxr-xr-x) | Owner can read/write/execute; others can read/execute |
| Files | 644 (rw-r--r--) | Owner can read/write; others can read |
| wp-config.php | 640 (rw-r-----) | Restrict access to owner and group only |
| wp-content/uploads/ | 755 | Must be writable by PHP-FPM user |
Alternative: Define FS_METHOD in wp-config.php
You can force WordPress to use the direct filesystem method by adding this to wp-config.php:
define('FS_METHOD', 'direct');
This tells WordPress to skip the ownership check and always write files directly. However, this only works if PHP actually has write permissions to the files. If the permissions are wrong, you will get "Could not create directory" errors instead of the FTP prompt.
Important: Using FS_METHOD direct is a workaround, not a fix. The proper solution is to ensure the PHP-FPM user matches the file owner so that the ownership check passes naturally.
Verifying Your PHP-FPM Configuration
# Check which user PHP-FPM is running as:
ps aux | grep php-fpm
# Example output:
# root 1234 ... php-fpm: master process
# www-data 1235 ... php-fpm: pool www
# wpuser 1236 ... php-fpm: pool wordpress
# Check the file ownership:
ls -la /var/www/wordpress/wp-config.php
# -rw-r----- 1 wpuser wpuser 3456 Jan 15 10:30 wp-config.php
# Verify PHP-FPM pool user matches file owner:
# Pool "wordpress" runs as "wpuser" ✓
# Files owned by "wpuser" ✓
# = WordPress will use direct filesystem method
Restarting PHP-FPM After Changes
# Restart PHP-FPM:
sudo systemctl restart php8.2-fpm
# Check for configuration errors:
sudo php-fpm8.2 -t
# Verify the service is running:
sudo systemctl status php8.2-fpm
Common Mistakes
- Forgetting to restart PHP-FPM after editing pool configuration. Pool settings are only loaded on start/restart.
- Setting permissions to 777. Never do this. It makes all files writable by any user on the system, creating a serious security vulnerability.
- Mixing chown and FS_METHOD. If you set
FS_METHODtodirectbut the PHP-FPM user cannot write to the files, updates will fail with filesystem errors. - Forgetting listen.owner. The socket must be readable by the web server (Nginx/Apache). If Nginx runs as
www-data, setlisten.owner = www-data.
For a full guide on setting up Nginx with PHP-FPM, see Nginx, PHP, and SSL on Ubuntu. For broader server hardening, see the Linux security checklist.