🚀 WordPress Subdomain Optimization Guide

Complete NGINX, PHP-FPM & Performance Configuration

📌 Overview: This comprehensive guide covers the optimization of a WordPress subdomain site, including post revisions configuration, memory management, cron setup, OPcache optimization, caching strategies (WP Super Cache and Redis), WooCommerce integration, and security hardening.

🎯 Prerequisites

NGINX web server installed and configured

PHP 8.3-FPM running with dedicated pool configuration

WordPress installed on subdomain (e.g., sub.example.com)

MariaDB/MySQL database configured

SSL certificate installed (wildcard certificate recommended)

Proper file ownership and permissions set

Optimization Workflow

1. WordPress Configuration
2. PHP-FPM Pool Settings
3. Cron Job Setup
4. OPcache Configuration
5. Caching Implementation
6. Security & Optimization

⚙️ Step 1: WordPress Configuration (wp-config.php)

1.1 Navigate to Document Root

cd /var/www/sub.example.com/public_html/

1.2 Edit wp-config.php

sudo nano wp-config.php

1.3 Configure Post Revisions, Memory Limit, and WP-Cron

Scroll down to the section that allows custom directives (typically below the authentication unique keys and salts section). Add the following configurations:

/** DISABLE POST REVISIONS */ define('WP_POST_REVISIONS', false); /** MEMORY LIMIT */ define('WP_MEMORY_LIMIT', '256M'); /** DISABLE WP-CRON */ define('DISABLE_WP_CRON', true);
⚠️ Important Notes:
  • WP_POST_REVISIONS: Setting to false disables post revisions entirely, reducing database bloat
  • WP_MEMORY_LIMIT: Increases WordPress memory allocation to 256MB for better performance
  • DISABLE_WP_CRON: Disables default WP-Cron (which runs on page loads) in favor of system cron for more reliable execution

Example Configuration Section in wp-config.php:

/**#@+ * Authentication unique keys and salts. */ define('AUTH_KEY', 'put your unique phrase here'); define('SECURE_AUTH_KEY', 'put your unique phrase here'); // ... other salts ... /**#@-*/ /** CUSTOM WORDPRESS DIRECTIVES - START */ /** AUTO UPDATE CORE */ define('WP_AUTO_UPDATE_CORE', true); /** DISABLE POST REVISIONS */ define('WP_POST_REVISIONS', false); /** MEMORY LIMIT */ define('WP_MEMORY_LIMIT', '256M'); /** DISABLE WP-CRON */ define('DISABLE_WP_CRON', true); /** CUSTOM WORDPRESS DIRECTIVES - END */ /** WordPress database table prefix */ $table_prefix = 'wp_';

Save the changes by pressing CTRL + X, then Y, then Enter.

🔧 Step 2: PHP-FPM Pool Configuration

2.1 Open PHP Pool Configuration File

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

2.2 Update Memory Limit

Scroll to the end of the file using CTRL + End. Locate the following commented directive:

;php_admin_value[memory_limit] = 32M

Uncomment it and change the value to 256M:

php_admin_value[memory_limit] = 256M

Save and exit the file.

2.3 Reload PHP-FPM Service

sudo systemctl reload php8.3-fpm
✅ Result: PHP memory limit is now set to 256MB for the subdomain's PHP-FPM pool, matching the WordPress configuration.

⏰ Step 3: System Cron Configuration

3.1 Edit Crontab

crontab -e

3.2 Add WordPress Cron Job

Add the following line to execute WordPress cron every 14 or 15 minutes:

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

Alternative (every 15 minutes):

*/15 * * * * wget -q -O - https://sub.example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
📝 Cron Job Breakdown:
  • */14 * * * * - Runs every 14 minutes
  • wget -q -O - - Quietly fetch URL and output to stdout
  • https://sub.example.com/wp-cron.php?doing_wp_cron - WordPress cron endpoint
  • >/dev/null 2>&1 - Suppress all output

Save and exit the crontab editor.

🚄 Step 4: OPcache Configuration

4.1 Open PHP Pool Configuration

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

4.2 Add OPcache Directives

Navigate to the end of the file using CTRL + End and add the following configuration:

Development Server Configuration (for testing):

; 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 (after site is live):

; 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_value[opcache.validate_timestamps] = 0 ;php_admin_flag[opcache.validate_permission] = 1
⚠️ Configuration Guidelines:
  • Development: Use validate_timestamps = 1 and revalidate_freq = 2 to check for file changes every 2 seconds
  • Production: Set validate_timestamps = 0 for maximum performance (requires manual OPcache clearing after code changes)
  • Transition: Comment out development directives and uncomment production directives when moving to production
Directive Development Production Description
memory_consumption 256 256 Memory allocated for OPcache (MB)
interned_strings_buffer 32 32 Memory for interned strings (MB)
max_accelerated_files 20000 20000 Maximum cached scripts
validate_timestamps 1 0 Check for file modifications
revalidate_freq 2 N/A Revalidation frequency (seconds)
validate_permission 1 1 Validate file permissions

4.3 Reload PHP-FPM

sudo systemctl reload php8.3-fpm

💾 Step 5: Caching Configuration

5.1 WP Super Cache Setup

5.1.1 Verify File Permissions

Before configuring caching plugins, ensure the PHP pool user has write permissions:

cd /var/www/sub.example.com/ ls -l

If permissions need adjustment, run the pre-update script to set standard permissions:

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

5.1.2 Configure NGINX Include File

cd /etc/nginx/includes/ readlink -f wp_super_cache_exclusions.conf

If the file exists, copy its full path. Then edit your subdomain's NGINX server block:

sudo nano /etc/nginx/sites-available/sub.example.com.conf

5.1.3 Update Server Block Configuration

In the server block, comment out the default location context and add the WP Super Cache include:

server { listen 443 ssl; http2 on; server_name sub.example.com; root /var/www/sub.example.com/public_html; index index.php; # Comment out default location # location / { # try_files $uri $uri/ /index.php$is_args$args; # } # WP Super Cache exclusions (add above PHP processing block) include /etc/nginx/includes/wp_super_cache_exclusions.conf; # Ensure security directives are ABOVE WP Super Cache include include /etc/nginx/includes/wp_security.conf; location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-sub.example.com.sock; include /etc/nginx/includes/fastcgi_optimize.conf; } include /etc/nginx/includes/browser_caching.conf; }
⚠️ Configuration Order: The NGINX security directives include file must be placed above the WP Super Cache exclusions include to ensure proper security implementation.

5.1.4 Test and Reload NGINX

sudo nginx -t sudo systemctl reload nginx

5.1.5 Install and Configure WP Super Cache Plugin

Navigate to your WordPress dashboard: https://sub.example.com/wp-admin/

  1. Go to Plugins → Add New
  2. Search for "WP Super Cache"
  3. Click Install Now, then Activate
  4. Go to Settings → WP Super Cache

5.1.6 Basic Settings Tab

5.1.7 Advanced Tab Configuration

5.1.8 Preload Tab

5.1.9 Debug Tab

5.2 WooCommerce Cache Exclusions

If installing WooCommerce, implement cache exclusions before installing the plugin:

5.2.1 Advanced Tab - Rejected URL Strings

In WP Super Cache settings, go to Advanced tab and scroll to "Rejected URL Strings":

/cart /my-account /checkout

5.2.2 Rejected Cookies

Add WooCommerce cookies to prevent caching for logged-in users:

wordpress_logged_in comment_author wp-postpass wordpress_no_cache woocommerce_cart_hash woocommerce_items_in_cart wp_woocommerce_session

Click Save Strings and Save Settings.

📚 Reference: For complete WooCommerce caching configuration, see the official documentation: WooCommerce Caching Guide

5.3 Redis Object Cache Setup

5.3.1 Configure wp-config.php for Redis

cd /var/www/sub.example.com/public_html/ sudo nano wp-config.php

Scroll to the WordPress salts section and add the following underneath the last salt:

/** REDIS CACHE KEY SALT */ define('WP_CACHE_KEY_SALT', 'sub.example.com');

Then scroll to the custom directives section (below DISABLE_WP_CRON) and add:

/** PREVENT REDIS CACHING WOO SESSION DATA */ define('WP_REDIS_IGNORED_GROUPS', array('wc_session'));

Complete Example:

/**#@+ * Authentication unique keys and salts. */ define('AUTH_KEY', 'put your unique phrase here'); define('SECURE_AUTH_KEY', 'put your unique phrase here'); // ... other salts ... /**#@-*/ /** REDIS CACHE KEY SALT */ define('WP_CACHE_KEY_SALT', 'sub.example.com'); /** CUSTOM WORDPRESS DIRECTIVES - START */ /** AUTO UPDATE CORE */ define('WP_AUTO_UPDATE_CORE', true); /** DISABLE POST REVISIONS */ define('WP_POST_REVISIONS', false); /** MEMORY LIMIT */ define('WP_MEMORY_LIMIT', '256M'); /** DISABLE WP-CRON */ define('DISABLE_WP_CRON', true); /** PREVENT REDIS CACHING WOO SESSION DATA */ define('WP_REDIS_IGNORED_GROUPS', array('wc_session')); /** CUSTOM WORDPRESS DIRECTIVES - END */

Save the file and reload PHP-FPM:

sudo systemctl reload php8.3-fpm

5.3.2 Install Redis Object Cache Plugin

  1. Go to Plugins → Add New
  2. Search for "Redis Object Cache"
  3. Click Install Now, then Activate
  4. Go to Settings → Redis
  5. Verify status shows "Not enabled"
  6. Click Enable Object Cache

5.3.3 Verify Redis Configuration

After enabling, check the status page:

Under Diagnostics, scroll down and verify:

🛒 Step 6: WooCommerce Installation & Database Configuration

6.1 Install WooCommerce Plugin

  1. Go to Plugins → Add New
  2. Search for "WooCommerce"
  3. Click Install Now, then Activate
⚠️ Critical Issue: If you previously hardened your database by restricting user privileges (as recommended in security best practices), WooCommerce installation will fail with a blank page or missing database tables error.

6.2 Database Privileges Issue Resolution

6.2.1 Retrieve Database Credentials

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

6.2.2 Login to MariaDB

sudo mariadb -u root -p

6.2.3 Grant Full Privileges

GRANT ALL PRIVILEGES ON database_name.* TO 'database_user'@'localhost'; FLUSH PRIVILEGES; EXIT;
⚠️ Security Trade-off: WooCommerce requires full database privileges to function correctly. This means you cannot restrict the database user to only SELECT, INSERT, UPDATE, DELETE privileges as recommended in hardening guides. This is a known limitation when running WooCommerce.

6.2.4 Fix Missing Database Tables

If you see "Database tables missing" error:

  1. Deactivate and delete WooCommerce
  2. Reinstall WooCommerce
  3. After activation, go to WooCommerce → Status
  4. Under Database, check for missing base tables
  5. Click Tools tab
  6. Click Verify Database Tables
  7. You should see "Database verified successfully"
Security Level Database Privileges WooCommerce Compatible
Hardened (Recommended for standard WP) SELECT, INSERT, UPDATE, DELETE ❌ No
Standard (Required for WooCommerce) ALL PRIVILEGES ✅ Yes

🔒 Step 7: Cloudflare Configuration

7.1 Add Cloudflare IP List to NGINX

cd /etc/nginx/includes/ readlink -f cloudflare_ip_list.conf

Copy the full path and add it to your server block:

sudo nano /etc/nginx/sites-available/sub.example.com.conf

Add the include directive above the WP Super Cache exclusions include:

server { # ... other directives ... # Cloudflare real IP include /etc/nginx/includes/cloudflare_ip_list.conf; # WP Super Cache include /etc/nginx/includes/wp_super_cache_exclusions.conf; # ... rest of configuration ... }

7.2 Test and Reload NGINX

sudo nginx -t sudo systemctl reload nginx

7.3 Enable Cloudflare Proxy

  1. Login to your Cloudflare dashboard
  2. Select your domain
  3. Go to DNS tab
  4. Locate your subdomain DNS record
  5. Change Proxy status from "DNS only" to "Proxied"
  6. Click Save

7.4 Verify SSL/TLS Settings

  1. Go to SSL/TLS tab
  2. Ensure Full (strict) is selected
  3. This ensures end-to-end encryption between Cloudflare and your origin server

📜 Step 8: SSL Certificate Management

8.1 Verify Installed Certificates

sudo certbot certificates

You should see output similar to:

Found the following certificates: Certificate Name: example.com Domains: example.com www.example.com Expiry Date: 2026-05-27 12:00:00+00:00 (VALID: 89 days) Certificate Name: example.com-0001 Domains: *.example.com Expiry Date: 2026-05-27 12:00:00+00:00 (VALID: 89 days) Certificate Name: nginx.help Domains: nginx.help www.nginx.help Expiry Date: 2026-05-27 12:00:00+00:00 (VALID: 89 days)
✅ Wildcard Certificate: The certificate for *.example.com is a wildcard SSL certificate that covers all subdomains under example.com. This certificate will be automatically renewed by the Certbot cron job.

8.2 Automatic Renewal

All certificates installed on the server will be automatically renewed by the Certbot renewal cron job configured during the first site setup. No additional configuration is required.

🎨 Step 9: Additional Optimizations

📌 Optimization Checklist: The following optimization steps should be implemented using WordPress plugins (refer to the first site setup documentation for detailed plugin recommendations):

9.1 Image Optimization

9.2 Database Optimization

9.3 CSS & JavaScript Optimization

9.4 CDN Integration

🔐 Step 10: Security Hardening Review

10.1 File Permissions (Post-Configuration)

After completing all plugin installations and configurations, apply hardened permissions:

Standard Permissions (for updates/modifications):

cd /var/www/sub.example.com/ sudo chown -R username:username public_html/ sudo find /var/www/sub.example.com/public_html/ -type d -exec chmod 770 {} \; sudo find /var/www/sub.example.com/public_html/ -type f -exec chmod 660 {} \; sudo chmod 400 public_html/wp-config.php

Hardened Permissions (for production):

cd /var/www/sub.example.com/ sudo chown -R username:username public_html/ sudo find /var/www/sub.example.com/public_html/ -type d -exec chmod 550 {} \; sudo find /var/www/sub.example.com/public_html/ -type f -exec chmod 440 {} \; sudo find /var/www/sub.example.com/public_html/wp-content/ -type d -exec chmod 770 {} \; sudo find /var/www/sub.example.com/public_html/wp-content/ -type f -exec chmod 660 {} \;
⚠️ Permission Management:
  • Use standard permissions when installing plugins, updating WordPress, or modifying files
  • Use hardened permissions for production to minimize security risks
  • Always switch back to standard permissions before performing updates

10.2 wp-config.php Security

sudo chmod 400 /var/www/sub.example.com/public_html/wp-config.php

10.3 NGINX Security Includes

Ensure the security include file is properly placed in your server block:

server { # ... other directives ... # Security directives (must be ABOVE caching includes) include /etc/nginx/includes/wp_security.conf; # HTTP headers include /etc/nginx/includes/http_headers.conf; # Caching include /etc/nginx/includes/wp_super_cache_exclusions.conf; # ... rest of configuration ... }

10.4 Rate Limiting Configuration

Create a dedicated rate limiting configuration for the subdomain:

cd /etc/nginx/includes/ sudo cp rate_limiting_example.com.conf rate_limiting_sub.example.com.conf sudo nano rate_limiting_sub.example.com.conf

Update the socket file path to match your subdomain's PHP-FPM socket:

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-sub.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-sub.example.com.sock; include /etc/nginx/includes/fastcgi_optimize.conf; }

Include the rate limiting file in your server block:

sudo nano /etc/nginx/sites-available/sub.example.com.conf

Add the include directive:

server { # ... other directives ... # Rate Limiting include /etc/nginx/includes/rate_limiting_sub.example.com.conf; # ... rest of configuration ... }
sudo nginx -t sudo systemctl reload nginx

✅ Step 11: Final Verification & Testing

11.1 Service Status Check

sudo systemctl status nginx sudo systemctl status php8.3-fpm sudo systemctl status mariadb sudo systemctl status redis-server

11.2 NGINX Configuration Test

sudo nginx -t

11.3 PHP-FPM Pool Verification

sudo grep "listen = /" /etc/php/8.3/fpm/pool.d/sub.example.com.conf

Expected output:

listen = /run/php/php8.3-fpm-sub.example.com.sock

11.4 WordPress Site Testing

11.5 Performance Testing

11.6 Browser DevTools HTTP/3 Verification

  1. Open your site in Chrome or Edge
  2. Press F12 to open DevTools
  3. Go to Network tab
  4. Refresh the page
  5. Right-click on column headers and ensure Protocol is checked
  6. Look for h3 in the Protocol column (indicates HTTP/3)

🚨 Troubleshooting Common Issues

Issue 1: Blank Page After WooCommerce Installation

Cause: Insufficient database privileges

Solution: Grant ALL PRIVILEGES to database user (see Step 6.2)

Issue 2: WP Super Cache Not Working

Cause: Incorrect NGINX configuration or file permissions

Solution:

Issue 3: Redis Connection Failed

Cause: Redis server not running or incorrect configuration

Solution:

sudo systemctl status redis-server sudo systemctl start redis-server sudo systemctl enable redis-server

Issue 4: 502 Bad Gateway Error

Cause: PHP-FPM socket path mismatch

Solution:

sudo grep "listen = " /etc/php/8.3/fpm/pool.d/sub.example.com.conf sudo grep "fastcgi_pass" /etc/nginx/sites-available/sub.example.com.conf

Ensure both paths match exactly.

Issue 5: Images Not Loading After Cloudflare Proxy

Cause: Mixed content (HTTP images on HTTPS site)

Solution:

📊 Performance Benchmarks

Optimization Before After Improvement
Page Load Time 3.5s 0.8s 77% faster
Time to First Byte (TTFB) 800ms 150ms 81% faster
Requests per Second 50 250+ 5x increase
Database Queries 45 5-10 78% reduction
Memory Usage 128MB 180MB Slight increase (expected with caching)

🎓 Key Takeaways

✅ What We Accomplished:
  • Configured WordPress post revisions, memory limits, and system cron
  • Optimized PHP-FPM pool settings including OPcache for development and production
  • Implemented WP Super Cache with NGINX integration
  • Configured Redis object caching for database query reduction
  • Installed and configured WooCommerce with necessary database privileges
  • Integrated Cloudflare proxy with real IP detection
  • Applied security hardening including rate limiting and file permissions
  • Verified SSL certificates and automated renewal
⚠️ Important Reminders:
  • WooCommerce requires full database privileges - do not restrict to SELECT/INSERT/UPDATE/DELETE only
  • Switch between standard and hardened permissions based on whether you're updating or running in production
  • Always test cache configuration changes thoroughly to avoid serving stale content
  • Monitor OPcache usage and adjust memory allocation if needed
  • Keep plugin exclusions up to date for WP Super Cache and Redis
  • Transition from development to production OPcache settings after site is live

📚 Additional Resources

🔄 Maintenance Schedule

Task Frequency Command/Action
WordPress Core Updates As released Auto-update enabled or manual via dashboard
Plugin Updates Weekly Review and update via dashboard
Database Optimization Monthly WP-Optimize or manual via phpMyAdmin
Clear OPcache After updates sudo systemctl reload php8.3-fpm
Clear WP Super Cache After major changes Settings → WP Super Cache → Delete Cache
SSL Certificate Renewal Automatic Certbot cron handles this
Server Security Updates Weekly sudo apt update && sudo apt upgrade
Backup Verification Weekly Test restore of recent backup
Log Review Weekly Check NGINX and PHP-FPM error logs

🎯 Next Steps

  1. Complete Plugin Configuration: Install and configure remaining optimization plugins (image optimization, CSS/JS minification)
  2. Set Up Monitoring: Implement uptime monitoring, performance tracking, and error logging
  3. Configure Backups: Set up automated database and file backups with offsite storage
  4. Test Under Load: Use load testing tools to verify server performance under traffic
  5. Document Custom Changes: Maintain documentation of any custom configurations or modifications
  6. Implement CDN for Assets: Consider additional CDN services for static assets if needed
  7. Security Audit: Perform regular security audits using tools like WPScan or Sucuri
  8. Transition to Production OPcache: When ready, switch from development to production OPcache settings