NGINX WordPress Server Configuration Guide

Section 19: Hosting Additional Sites & Domains

angelscript

📋 Overview

This comprehensive guide covers the complete process of adding additional WordPress sites to your NGINX server. The process includes setting up directories, configuring databases, hardening security, and optimizing performance.

Complete Deployment Workflow

1. Create Site Directories
2. Setup Database & Credentials
3. Configure NGINX Server Block
4. Install WordPress
5. Implement Security Hardening
6. Configure SSL/TLS
7. Optimize Performance

🔧 Bash Scripts Setup

Creating automated bash scripts streamlines the process of adding new sites to your server. These scripts handle directory creation, database setup, and configuration file generation.

Initial Setup

cd mkdir wp_bash_scripts/ cd wp_bash_scripts/

Script 1: Create Site Directories

nano 1.create_wp_directories.sh
#!/bin/bash Function to create site directories create_site_directories() { domain=$1 # Check if the directory already exists if [ ! -d "/var/www/$domain" ]; then # Create the directories with appropriate permissions sudo mkdir -p "/var/www/$domain/public_html" sudo mkdir -p "/var/www/$domain/tmp" echo "Site directory for $domain created successfully." else echo "Site directory for $domain already exists." fi } Main script starts here Ask for domain name read -p "Enter domain name: " domain_name Call function to create site directories create_site_directories "$domain_name"
angelscript
💡 What This Script Does:
  • Creates a directory structure for your domain under /var/www/
  • Establishes public_html for web files
  • Creates tmp directory for temporary files
  • Checks for existing directories to prevent overwrites

Script 2: Create WordPress Database

nano 2.create_wp_database.sh
#!/bin/bash echo "DO NOT USE A PERIOD IN THE DATABASE NAME" echo "use an underscore in place of a period" echo "What is your domain name?" read domain DB="$domain" create random database user DBUSER="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1)" create random password PASSWORD="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1)" create random database prefix PREFIX="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 4 | head -n 1)" mysql < angelscript
⚠️ Important Security Notes:
  • The script generates random credentials for enhanced security
  • Database usernames and passwords are 12 characters long
  • Save these credentials immediately - they cannot be recovered
  • Never use periods in database names; use underscores instead

Script 3: Generate Admin Credentials

nano 3.admin_credentials.sh
#!/bin/bash Generate and display WordPress admin username and password echo "Randomly Generated 30 Character WordPress Administrative Credentials" echo "" echo "WordPress Admin Username: $(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 30 | head -n 1)" echo "WordPress Admin Password: $(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 30 | head -n 1)" Fetch WordPress salts wp_salts=$(curl -s https://api.wordpress.org/secret-key/1.1/salt/) Add WP constants wp_constants=$(cat << 'EOF' /** Allow Direct Updating Without FTP / define('FS_METHOD', 'direct' ); /* Disable Editing of Themes and Plugins Using the Built In Editor / define('DISALLOW_FILE_EDIT', 'true' ); /* TURN OFF AUTOMATIC UPDATES */ define('WP_AUTO_UPDATE_CORE', 'false' ); EOF ) Output the salts and constants echo "" echo "WordPress Salts" echo "" echo "$wp_salts" echo "" echo "Constants to be added to wp-config.php" echo "" echo "$wp_constants"
angelscript
💡 Script Features:
  • Generates 30-character admin credentials for maximum security
  • Retrieves fresh WordPress salts from official API
  • Provides security constants for wp-config.php
  • Disables file editing through WordPress admin panel

Script 4: NGINX Server Block Configuration

nano 4.nginx_server_block.sh
#!/bin/bash Function to create directories and files if they don't exist create_files_and_directories() { # Check if the includes directory exists, if not create it if [ ! -d "/etc/nginx/includes" ]; then sudo mkdir -p /etc/nginx/includes fi # Check if fastcgi_optimize.conf exists, if not create it if [ ! -f "/etc/nginx/includes/fastcgi_optimize.conf" ]; then sudo tee /etc/nginx/includes/fastcgi_optimize.conf > /dev/null <<EOF fastcgi_connect_timeout 60; fastcgi_send_timeout 180; fastcgi_read_timeout 180; fastcgi_buffer_size 512k; fastcgi_buffers 512 16k; fastcgi_busy_buffers_size 1m; fastcgi_temp_file_write_size 4m; fastcgi_max_temp_file_size 4m; fastcgi_intercept_errors on; EOF fi # Check if browser_caching.conf exists, if not create it if [ ! -f "/etc/nginx/includes/browser_caching.conf" ]; then sudo tee /etc/nginx/includes/browser_caching.conf > /dev/null <<EOF location ~* .(webp|3gp|gif|jpg|jpeg|png|ico|wmv|avi|asf|asx|mpg|mpeg|mp4|pls|mp3|mid|wav|swf|flv|exe|zip|tar|rar|gz|tgz|bz2|uha|7z|doc|docx|xls|xlsx|pdf|iso)$ { add_header Cache-Control "public, no-transform"; access_log off; expires 365d; } location ~* .(js)$ { add_header Cache-Control "public, no-transform"; access_log off; expires 30d; } location ~* .(css)$ { add_header Cache-Control "public, no-transform"; access_log off; expires 30d; } location ~* .(eot|svg|ttf|woff|woff2)$ { add_header Cache-Control "public, no-transform"; access_log off; expires 30d; } EOF fi } Function to create domain.com.conf file create_domain_conf_file() { domain=$1 filename="/etc/nginx/sites-available/$domain.conf" perl # Define server_name without checking for subdomain server_name="$domain www.$domain" # Check if the domain.com.conf file exists, if not create it if [ ! -f "$filename" ]; then sudo tee "$filename" > /dev/null <<EOF server { awk listen 80; server_name $server_name; root /var/www/$domain/public_html; index index.php; location / { try_files \$uri \$uri/ /index.php\$is_args\$args; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; include /etc/nginx/includes/fastcgi_optimize.conf; } include /etc/nginx/includes/browser_caching.conf; access_log /var/log/nginx/access_$domain.log combined buffer=256k flush=60m; error_log /var/log/nginx/error_$domain.log; } EOF fi awk # Create a symbolic link to the sites-enabled directory sudo ln -s /etc/nginx/sites-available/$domain.conf /etc/nginx/sites-enabled/ } Main script starts here Ask for domain name read -p "Enter domain name: " domain_name Call function to create directories and files create_files_and_directories Call function to create domain.com.conf file create_domain_conf_file "$domain_name" Display success message echo "Configuration files created successfully."
angelscript
✅ After Running This Script:
  • NGINX server block configuration is created
  • FastCGI optimization settings are applied
  • Browser caching rules are configured
  • Access and error logs are set up

Validate NGINX Configuration

Always test your NGINX configuration before reloading:

sudo nginx -t sudo systemctl reload nginx

🌐 WordPress Installation

Download WordPress

cd wget https://wordpress.org/latest.tar.gz ls

Extract and Configure

tar xf latest.tar.gz ls cd wordpress/ ls mv wp-config-sample.php wp-config.php ls

Edit Configuration File

nano wp-config.php
💡 Configuration Changes Required:
  • Database Name: Enter the database name from script 2
  • Database User: Enter the generated username
  • Database Password: Enter the generated password
  • Salts: Replace with salts from script 3
  • Table Prefix: Use the generated prefix (e.g., abc1_)
  • WP Constants: Add security constants from script 3

Deploy WordPress Files

cd rsync -artv wordpress/ /var/www/example.com/public_html/ cd /var/www/example.com/ sudo chown -R www-data:www-data public_html/ ls -l cd public_html/ ls -l
✅ Next Step: Complete the WordPress installation through your web browser by navigating to your domain name.

🔒 WordPress Hardening

Security Hardening Components

1

PHP-FPM Pools

Isolate each site with dedicated PHP pools

2

File Permissions

Restrict access to critical files

3

Database Security

Limit database privileges

4

SSL/TLS

Encrypt all communications

PHP-FPM Pool Configuration

Create Dedicated User

sudo useradd username sudo usermod -a -G username www-data sudo usermod -a -G www-data username sudo usermod -a -G $USER username

Create Pool Configuration

cd /etc/php/8.3/fpm/pool.d/ ls sudo cp www.conf example.com.conf ls sudo nano example.com.conf
; pool name [example] ; Username and Group user = username group = username ; Path and Unix Socket Filename - unique to this pool listen = /run/php/php8.3-fpm-example.com.sock ; Set open file descriptor rlimit. rlimit_files = 15000 ; Set max core size rlimit. rlimit_core = 100 ; PHP POOL CONFIGURATION php_flag[display_errors] = off php_admin_value[error_log] = /var/log/fpm-php.example.log php_admin_flag[log_errors] = on
angelscript

Create Log File

sudo touch /var/log/fpm-php.example.log sudo chown username:www-data /var/log/fpm-php.example.log sudo chmod 660 /var/log/fpm-php.example.log

Update NGINX Configuration

sudo grep "listen = /" example.com.conf sudo nano /etc/nginx/sites-available/example.com.conf

Modify the fastcgi_pass directive:

fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;

Test and Reload Services

sudo nginx -t sudo systemctl reload nginx sudo systemctl reload php8.3-fpm

File Ownership and Permissions

Standard Permissions (for updates)

cd /var/www/example.com/ ls -l sudo chown -R username:username public_html/ tmp/ sudo chmod 770 public_html/ sudo chmod 770 tmp/ sudo find /var/www/example.com/public_html/ -type d -exec chmod 770 {} \; sudo find /var/www/example.com/public_html/ -type f -exec chmod 660 {} \; ls -l

Hardened Permissions (production)

sudo find /var/www/example.com/public_html/ -type d -exec chmod 550 {} \; sudo find /var/www/example.com/public_html/ -type f -exec chmod 440 {} \; sudo find /var/www/example.com/public_html/wp-content/ -type d -exec chmod 770 {} \; sudo find /var/www/example.com/public_html/wp-content/ -type f -exec chmod 660 {} \;
⚠️ Permission Levels:
Mode Directories Files Usage
Standard 770 660 When updating WordPress, themes, or plugins
Hardened 550 440 Production mode - maximum security
wp-content 770 660 Always writable for uploads and cache

PHP Function Restrictions

Add to your PHP pool configuration file:

; ENABLED FUNCTIONS ; disk_free_space ; DISABLED FUNCTIONS php_admin_value[disable_functions] = shell_exec, opcache_get_configuration, opcache_get_status, disk_total_space, diskfreespace, dl, exec, passthru, pclose, pcntl_alarm, pcntl_exec, pcntl_fork, pcntl_get_last_error, pcntl_getpriority, pcntl_setpriority, pcntl_signal, pcntl_signal_dispatch, pcntl_sigprocmask, pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_strerror, pcntl_waitpid, pcntl_wait, pcntl_wexitstatus, pcntl_wifcontinued, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_uname, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, show_source, system
angelscript
sudo systemctl reload php8.3-fpm

Open Base Directory Restrictions

cd /etc/php/8.3/fpm/pool.d/ sudo nano example.com.conf
php_admin_value[upload_tmp_dir] = /var/www/example.com/tmp/ php_admin_value[sys_temp_dir] = /var/www/example.com/tmp/ php_admin_value[open_basedir] = /var/www/example.com/public_html/:/var/www/example.com/tmp/
angelscript

SSL/TLS Certificate Installation

sudo certbot certonly --webroot -w /var/www/example.com/public_html/ -d example.com -d www.example.com

Create SSL Configuration File

sudo nano /etc/nginx/ssl/ssl_certs_example.com.conf
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; SSL STAPLING ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
pgsql

Update Server Block for HTTPS

server { listen 80; server_name example.com www.example.com; # ADD REDIRECT TO HTTPS: 301 PERMANENT 302 TEMPORARY return 301 https://example.com$request_uri; } server { listen 443 ssl; http2 on; awk listen 443 quic; http3 on; server_name example.com www.example.com; root /var/www/example.com/public_html; index index.php; # ADD SITE AND ALL SSL INCLUDE FILES include /etc/nginx/ssl/ssl_certs_example.com.conf; include /etc/nginx/ssl/ssl_all_sites.conf; location / { try_files $uri $uri/ /index.php$is_args$args; } # Place ABOVE the php processing location block include /etc/nginx/includes/http_headers.conf; location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock; include /etc/nginx/includes/fastcgi_optimize.conf; } include /etc/nginx/includes/browser_caching.conf; access_log /var/log/nginx/access_example.com.log combined buffer=256k flush=60m; error_log /var/log/nginx/error_example.com.log; }
angelscript
sudo nginx -t sudo systemctl reload nginx

Verify SSL Configuration

curl -I http://example.com curl -I http://www.example.com curl -I https://www.example.com curl -I https://example.com
💡 Test Your SSL Certificate:

Rate Limiting

cd /etc/nginx/includes/ sudo cp rate_limiting_example.com.conf rate_limiting_example.net.conf sudo nano rate_limiting_example.net.conf
location = /wp-login.php { limit_req zone=wp burst=20 nodelay; limit_req_status 444; include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock; include /etc/nginx/includes/fastcgi_optimize.conf; } location = /xmlrpc.php { limit_req zone=wp burst=20 nodelay; limit_req_status 444; include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock; include /etc/nginx/includes/fastcgi_optimize.conf; }

Add to your NGINX server block:

# Rate Limiting Include include /etc/nginx/includes/rate_limiting_example.net.conf;
angelscript

Database Privilege Restriction

Verify Database Credentials

sudo grep DB_NAME /var/www/example.com/public_html/wp-config.php sudo grep DB_USER /var/www/example.com/public_html/wp-config.php

Restrict Privileges

REVOKE ALL PRIVILEGES ON database_name.* FROM 'username'@'localhost'; GRANT SELECT, INSERT, UPDATE, DELETE ON database_name.* TO 'username'@'localhost'; FLUSH PRIVILEGES;
angelscript
⚠️ Database Security Note:

Restricting privileges prevents the database user from performing dangerous operations like DROP, ALTER, or CREATE. This limits damage from SQL injection attacks.

⚡ Performance Optimization

Optimization Stack

1

WordPress Config

Post revisions, memory limits, cron

2

OPcache

PHP bytecode caching

3

FastCGI Cache

Full-page caching

4

HTTP/3 & QUIC

Modern protocols

WordPress Configuration Optimizations

Disable Post Revisions

Add to wp-config.php:

define('WP_POST_REVISIONS', false);

Increase Memory Limit

Edit PHP pool configuration:

cd /etc/php/8.3/fpm/pool.d/ sudo nano example.com.conf
php_admin_value[memory_limit] = 256M

Add to wp-config.php:

/** MEMORY LIMIT */ define('WP_MEMORY_LIMIT', '256M');
routeros

Optimize WordPress Cron

Add to wp-config.php:

/** DISABLE WP-CRON */ define('DISABLE_WP_CRON', true);
angelscript

Setup system cron job:

crontab -e
*/15 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

OPcache Configuration

cd /etc/php/8.3/fpm/pool.d/ sudo nano example.com.conf

Development Server Configuration

; OPCACHE CONFIGURATION - DEVELOPMENT SERVER - Jul 2025 ; Directive php_admin_flag[opcache.enabled] leave commented - enabled by default ; php_admin_flag[opcache.enabled] = 1 php_admin_value[opcache.memory_consumption] = 256 php_admin_value[opcache.interned_strings_buffer] = 32 php_admin_value[opcache.max_accelerated_files] = 20000 php_admin_flag[opcache.validate_timestamps] = 1 php_admin_value[opcache.revalidate_freq] = 2 php_admin_flag[opcache.validate_permission] = 1

Production Server Configuration

; OPCACHE CONFIGURATION - PRODUCTION SERVER - Jul 2025 ; Directive php_admin_flag[opcache.enabled] leave commented - enabled by default ;php_admin_flag[opcache.enabled] = 1 ;php_admin_value[opcache.memory_consumption] = 256 ;php_admin_value[opcache.interned_strings_buffer] = 32 ;php_admin_value[opcache.max_accelerated_files] = 20000 ;php_admin_flag[opcache.validate_timestamps] = 0 ;php_admin_flag[opcache.validate_permission] = 1
angelscript
💡 OPcache Settings Explained:
Setting Value Description
memory_consumption 256 MB Memory allocated for OPcache
interned_strings_buffer 32 MB Memory for interned strings
max_accelerated_files 20000 Maximum number of cached files
validate_timestamps 0 (prod) / 1 (dev) Check for file changes
revalidate_freq 2 seconds How often to check timestamps

Permission Management Scripts

Script 5: Loosen Permissions (for updates)

cd cd wp_bash_scripts/ nano 5.loosen_permissions.sh
#!/bin/bash List directory contents of /var/www for reference sudo ls -l /var/www Prompt for the domain name read -p "Enter the domain name: " domain_name Check if the domain directory exists if [ ! -d "/var/www/$domain_name/public_html/" ]; then echo "Error: Domain directory /var/www/$domain_name/public_html/ does not exist." exit 1 fi Set permissions with confirmation echo "Setting permissions for /var/www/$domain_name/public_html/..." Grant read, write, and execute permissions to owner and group - directories sudo find "/var/www/$domain_name/public_html/" -type d -exec chmod 770 {} ; Grant read and write permissions to owner and group - files sudo find "/var/www/$domain_name/public_html/" -type f -exec chmod 660 {} ; echo "Permissions set to allow WordPress updates using the dashboard!"
angelscript

Script 6: Tighten Permissions (production mode)

cd cd wp_bash_scripts/ nano 6.tighten_permissions.sh
#!/bin/bash List directory contents of /var/www for reference sudo ls -l /var/www Prompt for the domain name read -p "Enter the domain name: " domain_name Check if the domain directory exists if [ ! -d "/var/www/$domain_name/public_html/" ]; then echo "Error: Domain directory /var/www/$domain_name/public_html/ does not exist." exit 1 fi Set permissions with confirmation echo "Setting permissions for /var/www/$domain_name/public_html/..." Grant read and execute permissions to the owner and group only sudo find "/var/www/$domain_name/public_html/" -type d -exec chmod 550 {} ; Grant read permissions to the owner and group only sudo find "/var/www/$domain_name/public_html/" -type f -exec chmod 440 {} ; Grant read, write and execute permissions to the owner and group only - wp-content/ sudo find "/var/www/$domain_name/public_html/wp-content" -type d -exec chmod 770 {} ; Grant read, write to the owner and group only - wp-content/ sudo find "/var/www/$domain_name/public_html/wp-content/" -type f -exec chmod 660 {} ; echo "Permissions hardened, no write permissions on core WordPress files and directories!"
angelscript

FastCGI Caching

Configure nginx.conf

cd /etc/nginx sudo nano nginx.conf
### FASTCGI CACHING fastcgi_cache_path directive - PATH & NAME must be unique for each site Add a new fastcgi_cache_path for each site and give a new keys_zone name fastcgi_cache_path /var/run/SITE levels=1:2 keys_zone=NAME:100m inactive=60m; fastcgi_cache_path /var/run/SITE2 levels=1:2 keys_zone=NAME2:100m inactive=60m; applied to all sites 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;
angelscript

Update Server Block

cd /etc/nginx/sites-available/ sudo nano example.com.conf
location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock; include /etc/nginx/includes/fastcgi_optimize.conf; # fastcgi caching directives fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; fastcgi_cache NAME; fastcgi_cache_valid 60m; } include /etc/nginx/includes/fastcgi_cache_excludes.conf; add_header X-FastCGI-Cache $upstream_cache_status; Cache purge endpoint location ~ /purge(/.*) { fastcgi_cache_purge NAME "$scheme$request_method$host$1"; }
angelscript
✅ FastCGI Cache Benefits:
  • Dramatically reduces server load
  • Serves cached pages in microseconds
  • Reduces database queries
  • Improves response time for visitors

📊 Monitoring & Maintenance

PHP-FPM Pool Memory Usage

cd ~/wp_bash_scripts/ nano pool_list_usage.sh
#!/bin/bash Extract pool names pools=$(grep -E '^\suser\s=' /etc/php/8.3/fpm/pool.d/*.conf | awk -F= '{print $2}' | xargs | tr ' ' '\n' | sort -u) Display memory used by each pool for pool in $pools; do memory_used=$(ps -C php-fpm --user "$pool" -o rss= | awk '{ sum += $1; count++ } END { if (count > 0) printf ("%d%s\n", sum/NR/1024,"M") }') echo -e "Pool: $pool\nMemory Used: $memory_used\n" done
angelscript

OPcache File Count Monitoring

nano opcache_files.sh
#!/bin/bash echo "" echo "Checking for and displaying the opcache.max_accelerated_files directive value in each sites PHP pool file" echo "" POOL_DIR="/etc/php/8.3/fpm/pool.d/" for file in "$POOL_DIR"*.conf; do filename=$(basename "$file") if grep -q "^[^#;]*opcache.max_accelerated_files" "$file"; then value=$(grep "^[^#;]*opcache.max_accelerated_files" "$file" | awk -F'=' '{print $2}' | tr -d ' ') echo "File: $filename - opcache.max_accelerated_files: $value" else echo "File: $filename - opcache.max_accelerated_files directive not found" fi done echo "" echo "Listing sites and number of PHP files in each site, compare to the value set in the pool file" echo "" WWW_DIR="/var/www/" for domain_dir in "$WWW_DIR"*/ ; do if [ "$domain_dir" == "/var/www/html/" ]; then continue fi routeros domain_name=$(basename "$domain_dir") public_html_dir="$domain_dir/public_html/" if [ -d "$public_html_dir" ]; then php_file_count=$(find "$public_html_dir" -type f -name "*.php" | wc -l) echo "Domain: $domain_name - PHP files: $php_file_count" else echo "Domain: $domain_name - public_html directory not found" fi done echo ""
angelscript
⚠️ OPcache Max Accelerated Files:

The directive value of 20,000 actually represents 32,000 files due to how PHP calculates the nearest prime number. Always ensure the number of PHP files in your site is lower than this value for optimal performance.

  • Run this script monthly to monitor file counts
  • If a site approaches 15,000-16,000 files, increase the max_accelerated_files value
  • Restart PHP-FPM after making changes

Example Output

Checking for and displaying the opcache.max_accelerated_files directive value in each sites PHP pool file File: expert.wp.help.conf - opcache.max_accelerated_files: 20000 File: nginx.help.conf - opcache.max_accelerated_files: 20000 File: www.conf - opcache.max_accelerated_files directive not found Listing sites and number of PHP files in each site, compare to the value set in the pool file Domain: expert.wp.help - PHP files: 1432 Domain: nginx.help - PHP files: 1575
angelscript

Maintenance Best Practices

💡 Regular Maintenance Tasks:
  • Weekly: Review error logs, monitor resource usage
  • Monthly: Run OPcache file count script, check for WordPress updates
  • Quarterly: Review and optimize database, check SSL certificate expiration
  • Semi-annually: Security audit, performance benchmarking

Maintenance Workflow

Loosen Permissions
Update WordPress/Plugins/Themes
Test Functionality
Tighten Permissions
Clear Caches
Monitor Performance

📝 Quick Reference Commands

Task Command
Test NGINX config sudo nginx -t
Reload NGINX sudo systemctl reload nginx
Reload PHP-FPM sudo systemctl reload php8.3-fpm
Check NGINX status sudo systemctl status nginx
View NGINX error log sudo tail -f /var/log/nginx/error.log
View site-specific error log sudo tail -f /var/log/nginx/error_example.com.log
List PHP pools ls /etc/php/8.3/fpm/pool.d/
Check file ownership ls -l /var/www/example.com/
Generate SSL cert sudo certbot certonly --webroot
Renew SSL certs sudo certbot renew

🎯 Summary

This guide has covered the complete process of adding and securing additional WordPress sites on your NGINX server. By following these steps, you've implemented:

✅ Automation

Bash scripts for consistent deployments

🔒 Security

Isolated PHP pools, restricted permissions, SSL/TLS

⚡ Performance

OPcache, FastCGI caching, HTTP/3

📊 Monitoring

Scripts for ongoing maintenance

✅ You're Ready!

Your server is now configured to host multiple WordPress sites with enterprise-level security and performance. Remember to run the maintenance scripts regularly and keep all software updated.