I described the installation of multi-site WordPress here with nginx webserver and php-fpm processes both running as user www-data. According to WordPress, the webserver should have read access to all data and only the WordPress user should have write access. The concept of a WordPress user relates to hosted environments. So how to implement these requirements on your own VPS?

First of all, php-fpm supports the concept of pools. Each pool can run with an unique user, separate from the webserver user. So each site should run in a separate pool. This results in the following accounts:

webserver: www-data user (default for nginx on debian)
site 1: site1_php user (site1 php pool)
site 2: site2_php user (site2 php pool)

The site1_php user owns all files in /var/www/site1 and the site2_php user owns all files in /var/www/site2. Then add the www-data user to groups site1_php and site2_php. This way, the www-data user can read all files but cannot write since it’s not owning any files and directories. This assumes group permissions are set to read, which is the default umask setting. This results in the following configuration steps. I explained the adduser options here. Note that site[1,2] is just shorthand for two lines, one with site1 and one with site2.

-> Create the accounts site1_php and site2_php as system user
sudo adduser --system --no-create-home --home /nonexistent --group site[1,2]_php
-> Add user www-data to groups site1_php and site2_php
usermod -a -G site[1,2]_php www-data
-> Set the ownership for site1 and site2
chown -R site[1,2]_php:site[1,2]_php /var/www/site[1,2]
-> Set permissions to 440 for files and 550 for directories
chmod -R u=rX,g=rX,o= /var/www/site[1,2]
-> Add write permission to wp-content dir and files
chmod -R u+w /var/www/site[1,2]/wp-content
-> Add write permission to site root
-> otherwise WordPress updates will try FTP
chmod u+w /var/www/site[1,2]/
—> For Duplicator add write permission to wp-snapshots
chmod -R u+w /var/www/site[1,2]/wp-snapshots

After this initial permissions juggling, what happens when files are created, for example by installing or upgrading a plugin? User site1_php will still create files with 644 and directories with 755 permission, this is defined by the umask setting. And PHP code can (and most likely will) change permissions with chmod(). So you can try to set a new umask value in the services file or something, but PHP can easily overwrite this.

Luckily WordPress has acknowledged this permissions problem (being a security issue on shared hosts) and provides a setting to ‘limit’  permissions set by chmod() in PHP:

—> To set permissions for new WordPress files and directories
-> add the following to wp-config.php
define( 'FS_CHMOD_DIR', ( 0750 & ~ umask() ) );
define( 'FS_CHMOD_FILE', ( 0640 & ~ umask() ) );

Next configure the php-fpm pools for multi-site:

—> Create new pool config files, one for each site
cd /etc/php/7.3/fpm/pool.d/
cp www.conf site[1,2].conf
—> Disable the default www pool
mv www.conf www.conf_notused
-> Update each pool config file and
-> set pool name to [site1] and [site2]
listen = /var/run/php/php7.3-fpm-site[1,2].sock
listen.owner = site[1,2]_php
listen.group = site[1,2]_php
listen.mode = 0660
user = site[1,2]_php
group = site[1,2]_php
-> Update the following line in the nginx config for each site
-> to match the 'listen' info in the php pool config
fastcgi_pass unix:/run/php/php7.3-fpm-site[1,2].sock;

Now restart nginx and php-fpm and verify process ownership and nginx to php-fpm communication. Note that these WordPress permissions will block automatic WP updates, since the wp-admin and wp-include directories are read-only. It’s up to you if you want to manually change that as part of your WP update process, or set these directories to 750 (like wp-content) to enable automatic updates to work. Note the capital X with the chmod command, this is your next step to unix guru status…

Leave a Reply

Your email address will not be published. Required fields are marked *

20 − 17 =