How To Secure Multiple WordPress Sites On Same Server (LEMP Stack)

Hi :slight_smile:

There is a known issue with installing multiple sites on the same server. i.e. if somebody gains access to one site, then he/she can affect other sites too.

How To Reproduce The Security Issue?

  • Install two WordPress sites on the same server with LEMP or LAMP stack.

  • Login to any site (let site1), install any file manager plugin, go to preferences and change public root path to β€œ/var/www”

  • Now you can edit files of all sites hosted on that server.

How To Solve It?

WordPress is a php based CMS, so when we run it using the master PHP-FPM process, it has all access rights which php has. It can access all other scripts/files which are using the same php process.

So here we are going to add different users for each website and create separate php-fpm pools for each website.

This will run a separate PHP-FPM process for each user/site and thus isolate them.

Here I am assuming that:-

  1. You had already installed two or more sites on LEMP stack. (let site1 and site2).
  2. PHP-FPM version is 7.4

Step 1

  • Create a group site1 and add user site1 to it.
sudo groupadd site1 
sudo useradd -g site1 site1 
  • Create a new pool configuration for user site1
nano /etc/php/7.4/fpm/pool.d/site1.conf
  • Paste configs in file. Press ctrl + o to save and ctrl + x to exit.
user = site1
group = site1
listen = /run/php/php7.4-fpm-site1.sock
listen.owner = www-data = www-data

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /
  • Disable php-opcache
nano /etc/php/7.4/fpm/conf.d/10-opcache.ini

add at the end of file--->  opcache.enable=0
  • Open server block of site1 (already configured server block of WordPress 1).
nano /etc/nginx/sites-available/site1
  • Edit this line β€œfastcgi_pass unix:/run/php/php7.4-fpm.sock;” as:-
fastcgi_pass unix:/run/php/php7.4-fpm-site1.sock;
  • Alter permissions of folder where site1 (WordPress1) is installed:-
sudo usermod -a -G site1 www-data
sudo chown -Rf site1:site1 /var/www/site1/
sudo chmod -R 750 /var/www/site1/
sudo find /var/www/site1/ -type f -exec chmod 644 {} \;
  • Restart PHP-FPM and Nginx
service nginx restart
systemctl restart php7.4-fpm.service

Step 2
Repeat same procedure for site2 , site3 … and all,


Now when you open file manager from site 1 (wordpress1), you should see something like this:-

Clearly, site1 can’t edit site2 and site3. Similarly site 2 can’t edit site1. But both site1 and site2 can edit there own files.


I am getting 502 error. Error log says unix:/run/php/php7.4-fpm-site4.sock failed (2: No such file or directory) while connecting to upstream

  • service php7.4-fpm restart

My new site redirecting to first one.

  • nginx -t && service nginx restart


Hope you find this tutorial helpful.

Thanks and Regards


Adding Multiple WordPress

  1. Follow above tip to create a new PHP Pool Configuration
nano /etc/php/7.4/fpm/pool.d/
  1. Create a new public directory for site 2 (let’s say it is
cd /var/www/ && mkdir && cd && mkdir public
  1. Download WordPress at /var/www/
cd /var/www/ && wget && tar -xvzf latest.tar.gz && mv -v wordpress/* /var/www/ && rm -rf index.nginx-debian.html latest.tar.gz wordpress
  1. Setup User, Group and Permission
sudo groupadd 
sudo useradd -g
sudo usermod -a -G www-data
sudo chown -Rf /var/www/
sudo chmod -R 750 /var/www/
sudo find /var/www/ -type f -exec chmod 644 {} \;
  1. Create a unique server block
# Set unique Cache path and Zone name
fastcgi_cache_path /var/www/ levels=1:2 keys_zone=blog_example_org:100m inactive=6h;

# do not repeat these
#fastcgi_cache_key "$scheme$request_method$host$request_uri";
#fastcgi_cache_use_stale error timeout invalid_header http_500;
#fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

server {
    server_tokens off;
    listen         80;
    return 301 https://$host$request_uri;
server {
listen 443;
server_tokens off;
index index.php index.html index.htm;

client_max_body_size 0;

## Assuming you have wildcard SSL for
## or get one
ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;

# define public path
root /var/www/;

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

set $skip_cache 0;
    if ($request_method = POST) {set $skip_cache 1;}
    if ($request_uri ~* "/wp-admin/|/xmlrpc.php|/wp-.*.php|index.php|sitemap(_index)?.xml") {set $skip_cache 1;}
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {set $skip_cache 1;}

location / {
        try_files $uri $uri/ /index.php?$args;

# Pass all .php files onto a php-fpm or php-cgi server
location ~* \.php$ {
        try_files                       $uri =404;
        include                         /etc/nginx/fastcgi_params;
        fastcgi_param                   SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass                    unix:/run/php/;   # Use unique Socket
        fastcgi_index                   index.php;
                fastcgi_cache_bypass $skip_cache;
                fastcgi_no_cache $skip_cache;
                fastcgi_cache blog_example_org;
                fastcgi_cache_valid 200 301 24h;
                add_header X-Cache $upstream_cache_status;


location = /favicon.ico {
log_not_found off;
access_log off;

location = /robots.txt {
log_not_found off;
access_log off;
allow all;

location ~* .(css|gif|ico|jpeg|jpg|js|png)$ {
expires 1y;
log_not_found off;

# Enable Gzip compression.
gzip on;

# Disable Gzip on IE6.
gzip_disable "msie6";

# Allow proxies to cache both compressed and regular version of file.
# Avoids clients that don't support Gzip outputting gibberish.
gzip_vary on;

# Compress data, even when the client connects through a proxy.
gzip_proxied any;

# The level of compression to apply to files. A higher compression level increases
# CPU usage. Level 5 is a happy medium resulting in roughly 75% compression.
gzip_comp_level 5;

# Compress the following MIME types.
  1. Symlink
ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/
  1. Configure NGINX Cache Directory for NGINX Helper Plugin
define('RT_WP_NGINX_HELPER_CACHE_PATH', '/var/www/');
  1. Create a new Database
sudo mysql -u root -p
create database blog_example_com;
grant all on blog_example_com.* to blog_example_com@localhost identified by 'TYPE PASSWORD';
flush privileges;
  1. Point DNS & visit /wp-admin/ for installation

  2. For security reasons, you can move wp-config.php one level up.

cd /var/www/
mv wp-config.php /var/www/
1 Like