WordPress Performance Optimization

Comprehensive Guide to Caching & Server Configuration

1. Introduction to WordPress Site Types

Understanding your WordPress site type is crucial for implementing the right caching strategy. WordPress sites generally fall into two categories: Static and Dynamic. Each requires different optimization approaches to maximize performance and user experience.

🗂️ Static WordPress Sites

A static WordPress site is one where content remains largely unchanged over time and is primarily informational. These sites are ideal for presenting fixed content that doesn't require frequent updates.

Characteristics:

  • Content changes infrequently
  • Primarily informational (company info, services, portfolios)
  • Minimal visitor interaction (contact forms, social media links)
  • No user-generated content or comments
  • Low maintenance requirements

Ideal Use Cases:

  • Corporate websites
  • Portfolio sites
  • Brochure websites
  • Landing pages

Recommended Caching Strategies:

  • Page Caching: Entire pages cached as HTML
  • Browser Caching: Static resources cached locally
  • Server-side Caching: Nginx FastCGI cache
  • CDN: Content delivered from edge servers

⚡ Dynamic WordPress Sites

A dynamic WordPress site features frequently updated content and interactive elements that enable real-time user engagement and personalized experiences.

Characteristics:

  • Content changes frequently
  • Interactive features (comments, forums, user accounts)
  • User-generated content
  • Real-time updates and interactions
  • Social media integration

Ideal Use Cases:

  • Blog sites with active comments
  • E-commerce platforms (WooCommerce)
  • News and magazine sites
  • Community forums
  • Membership sites

Recommended Caching Strategies:

  • Object Caching: Redis for database queries
  • Fragment Caching: Cache static parts only
  • Page Caching with Exceptions: Exclude dynamic content
  • CDN: Cache static assets only
Caching Strategy Decision Flow
WordPress Site
Analyze Content Update Frequency
Static Site
(Infrequent Updates)
Dynamic Site
(Frequent Updates)
Aggressive Caching
Full Page Cache
Nuanced Caching
Object + Fragment Cache
💡 Best Practice: By tailoring the caching strategy to your specific WordPress site type, you can optimize performance effectively, ensuring fast load times and a smooth user experience regardless of site complexity.

2. Basic WordPress Configuration

2.1 Disable Post Revisions

Post revisions can significantly increase database size over time. For most sites, disabling or limiting revisions is recommended.

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

Add the following line to your wp-config.php:

define('WP_POST_REVISIONS', 'false');

Restart PHP-FPM to apply changes:

sudo systemctl restart php8.3-fpm
Note: Cloudflare has deprecated their Auto-Minify feature. Reference: Cloudflare Community

2.2 Increase Memory Limit

Increasing PHP memory limits can prevent memory-related errors, especially for complex sites or plugins.

PHP Pool Configuration:

;php_admin_value[memory_limit] = 32M
php_admin_value[memory_limit] = 256M

WordPress Configuration:

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

2.3 Configure WP-Cron

By default, WordPress runs scheduled tasks on each page load. Disabling this and using system cron improves performance.

Add to wp-config.php:

define('DISABLE_WP_CRON', true);

Restart PHP-FPM:

sudo systemctl restart php8.1-fpm

Setup system cron job:

crontab -e

Add the following cron job (runs every 15 minutes):

*/15 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
WP-Cron Architecture
System Cron (Every 15 min)
Executes wp-cron.php
WordPress Scheduled Tasks
Publish Posts
Check Updates
Send Emails

3. OPcache Configuration

OPcache improves PHP performance by storing precompiled script bytecode in shared memory, eliminating the need for PHP to load and parse scripts on each request.

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

3.1 Development Server Configuration

For development environments, enable timestamp validation to automatically reload changed files.

; 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

3.2 Production Server Configuration

For production, disable timestamp validation for maximum performance.

; 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

Reload PHP-FPM:

sudo systemctl reload php8.3-fpm

3.3 Calculate Maximum Accelerated Files

Count your PHP files to set an appropriate max_accelerated_files value:

cd /var/www/
sudo find . -type f -print | grep php | wc -l
💡 Tip: Set max_accelerated_files to the next highest prime number after your PHP file count. Reference: PHP Documentation

OPcache Configuration Parameters Explained

Parameter Description Recommended Value
memory_consumption Amount of memory for OPcache (MB) 256
interned_strings_buffer Memory for interned strings (MB) 32
max_accelerated_files Maximum cached scripts 20000 (or prime number)
validate_timestamps Check file updates (1=dev, 0=prod) 1 (dev) / 0 (prod)
revalidate_freq Check frequency in seconds 2 (dev only)

4. FastCGI Caching with Nginx

FastCGI caching is a powerful server-level caching mechanism that stores the output of PHP scripts, significantly reducing server load and response times.

4.1 Configure Nginx HTTP Context

cd /etc/nginx
sudo nano nginx.conf

Add the following before the "# Virtual Host Configs" comment:

### 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;
# 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;

fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache NAME;
fastcgi_cache_valid 60m;

4.2 Create Cache Exclusion Rules

cd /etc/nginx/includes/
sudo nano fastcgi_cache_excludes.conf
# NGINX SKIP CACHE INCLUDE FILE
set $skip_cache 0;
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $skip_cache 1;
}
if ($query_string != "") {
    set $skip_cache 1;
}   
# Don't cache uris containing the following segments
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
    set $skip_cache 1;
}  
# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
    set $skip_cache 1;
}

4.3 Configure Server Block

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

Add these directives to your server block:

include /etc/nginx/includes/fastcgi_cache_excludes.conf;
add_header X-FastCGI-Cache $upstream_cache_status;

4.4 Test and Reload Nginx

sudo nginx -t
sudo systemctl reload nginx

4.5 Verify Caching

curl -I https://example.com
curl -I https://www.example.com
Note: Look for the "X-FastCGI-Cache" header in the response. Values can be: HIT (cached), MISS (not cached), BYPASS (excluded from cache), or EXPIRED (cache expired).

4.6 Setup Cache Purging

Add cache purge location to your server block:

location ~ /purge(/.*) {
    fastcgi_cache_purge NAME "$scheme$request_method$host$1";
}
sudo nginx -t
sudo systemctl reload nginx

4.7 Remove FastCGI Caching

If you need to disable FastCGI caching:

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

Comment out or remove:

  • include /etc/nginx/includes/fastcgi_cache_excludes.conf;
  • fastcgi_cache_bypass, fastcgi_no_cache, fastcgi_cache, and fastcgi_cache_valid directives
  • add_header X-FastCGI-Cache directive
  • Cache purge location block
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl restart php8.1-fpm
FastCGI Cache Flow
User Request
Nginx Receives Request
Check Cache Exclusions
Cache HIT
Return Cached
Cache MISS
Process PHP
Store in Cache (60m)
Return Response

5. WP Super Cache Configuration

WP Super Cache is a popular WordPress plugin that generates static HTML files from your dynamic WordPress site.

5.1 Create Exclusion Rules

cd /etc/nginx/includes/
sudo nano wp_super_cache_excludes.conf
# WP Super Cache NGINX Cache Exclusions Rules.
set $cache_uri $request_uri;
 
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $cache_uri 'null cache';
}

if ($query_string != "") {
    set $cache_uri 'null cache';
}

# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
}

# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
    try_files /wp-content/cache/supercache/$http_host/$cache_uri/index-https.html $uri $uri/ /index.php?$args ;
}

5.2 Configure Server Block

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

Add the include directive:

include /etc/nginx/includes/wp_super_cache_exclusions.conf;
sudo nginx -t
sudo systemctl reload nginx

5.3 Uninstall WP Super Cache

To remove WP Super Cache configuration:

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

Remove the include line and uncomment the default location block:

location / {
    try_files $uri $uri/ /index.php$is_args$args;
}
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl reload php8.3-fpm

6. W3 Total Cache Configuration

W3 Total Cache (W3TC) is a comprehensive caching plugin that offers page caching, object caching, database caching, and more.

6.1 Create Configuration File

cd /var/www/example.com/public_html/
sudo touch nginx.conf
sudo chown username:username nginx.conf
sudo chmod 660 nginx.conf
sudo ls -l

6.2 Secure nginx.conf File

cd /etc/nginx/includes/
sudo nano nginx_security_directives.conf

Add the following line:

location = /nginx.conf { deny all; }

6.3 Create Cache Exclusion Rules

cd /etc/nginx/includes/
sudo nano w3tc_cache_excludes.conf
# ---------------------
# W3 TOTAL CACHE EXCLUDES FILE 
# ---------------------
set $cache_uri $request_uri;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $cache_uri 'null cache';
}   
if ($query_string != "") {
    set $cache_uri 'null cache';
}   
# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
}   
# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
}
# Use cached or actual file if file exists, otherwise pass request to WordPress
location / {
    try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args;
}

6.4 Configure Server Block

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

Comment out default location block and add includes:

#location / {
#    try_files $uri $uri/ /index.php$is_args$args;
#}

include /etc/nginx/includes/w3tc_cache_excludes.conf;
include /var/www/example.com/public_html/nginx.conf;
sudo nginx -t
sudo systemctl reload nginx

6.5 Install PHP Tidy Extension

W3TC requires the PHP Tidy extension for HTML minification:

sudo apt update
sudo apt install php8.3-tidy
sudo systemctl reload nginx
sudo systemctl reload php8.3-fpm

6.6 Uninstall W3 Total Cache

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

Restore default location block and remove includes:

location / {
    try_files $uri $uri/ /index.php$is_args$args;
}

# Remove these lines:
# include /etc/nginx/includes/w3tc_cache_excludes.conf;
# include /var/www/example.com/public_html/nginx.conf;

Remove cache directories:

cd /var/www/example.com/public_html/wp-content/
sudo rm -rf cache/ w3tc-config/

Remove nginx.conf file:

cd /var/www/example.com/public_html/
sudo rm nginx.conf
sudo systemctl reload php8.3-fpm
sudo systemctl reload nginx

7. Redis Object Caching

Redis is an in-memory data structure store used as a database, cache, and message broker. It's excellent for object caching in WordPress.

7.1 Install Redis

sudo apt update
sudo apt install redis-server php8.3-redis

7.2 Verify Installation

sudo systemctl status redis-server
sudo cat /var/log/redis/redis-server.log
⚠️ Warning: If you see "WARNING overcommit_memory is set to 0! Background save may fail under low memory condition" in the log, follow the steps below.

7.3 Fix Overcommit Memory Warning

cd /etc/sysctl.d/
sudo nano 11-redis.conf

Add this line:

vm.overcommit_memory = 1
sudo reboot

After reboot, verify the fix:

sudo systemctl status redis-server
sudo cat /var/log/redis/redis-server.log

7.4 Configure Redis Memory

cd /etc/
sudo ls redis/
sudo nano redis/redis.conf

Find and set these values:

maxmemory 256mb
maxmemory-policy allkeys-lru
sudo systemctl restart redis-server
sudo reboot

7.5 Configure WordPress for Redis

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

Add cache key salt:

define( 'WP_CACHE_KEY_SALT', 'example.com' );

7.6 WooCommerce Redis Configuration

For WooCommerce sites, prevent Redis from caching session data:

/** PREVENT REDIS CACHING WOO SESSION DATA */
define('WP_REDIS_IGNORED_GROUPS', 'wc_session');
💡 Reference: For more WooCommerce caching configuration, see: WooCommerce Developer Docs

WooCommerce Cache Exclusions

URLs to exclude from cache:

  • /cart/
  • /my-account/
  • /checkout/

Cookies to exclude from cache:

  • woocommerce_cart_hash
  • woocommerce_items_in_cart
  • wp_woocommerce_session_
  • woocommerce_recently_viewed
  • store_notice[notice id]
  • _wc_session_
Redis Caching Architecture
WordPress Request
Check Redis Cache
Cache HIT
Return from Redis
Cache MISS
Query Database
Store Result in Redis
Return to User

8. PHP-FPM Optimization

Optimizing PHP-FPM process management ensures efficient resource utilization and prevents server overload.

8.1 Display Pool Usernames

grep -E '^\s*user\s*=' /etc/php/8.3/fpm/pool.d/*.conf | awk -F= '{print $2}' | xargs | tr ' ' '\n' | sort -u

8.2 Calculate Average Memory Usage

Replace POOL_USER with your actual pool username:

ps -C php-fpm --user POOL_USER -o rss= | awk '{ sum += $1; count++ } END { if (count > 0) printf ("%d%s\n", sum/NR/1024,"M") }'

8.3 Configure On-Demand Process Manager

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

Add or modify these directives:

pm = ondemand
pm.max_children = [calculated based on memory]
pm.process_idle_timeout = 10s
pm.max_requests = 500
sudo systemctl reload php8.3-fpm

8.4 Monitor max_children Warning

ls /var/log/
sudo grep max_children /var/log/php8.3-fpm.log
⚠️ Warning: If you see "WARNING: [pool www] server reached max_children setting (25), consider raising it", increase the pm.max_children value.

PHP-FPM Process Manager Modes

Mode Description Best For
static Fixed number of child processes High-traffic sites with predictable load
dynamic Adjusts children based on demand Sites with variable traffic
ondemand Spawns children when needed Low-traffic sites, resource conservation
💡 Calculating pm.max_children: Use the formula: pm.max_children = Total Available RAM / Average Process Memory. Leave some RAM for other services (MySQL, Redis, etc.).

9. Cloudflare Integration

When using Cloudflare as a reverse proxy, you need to configure Nginx to recognize the real visitor IP addresses.

9.1 Get Cloudflare IP Ranges

Cloudflare provides their IP ranges at:

9.2 Create Cloudflare IP Configuration

cd /etc/nginx/includes
sudo nano cloudflare_ip_list.conf
# Last updated OCT 2022
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
real_ip_header CF-Connecting-IP;

9.3 Include in Server Block

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

Add the include directive:

include /etc/nginx/includes/cloudflare_ip_list.conf;
sudo nginx -t
sudo systemctl reload nginx
Note: Cloudflare has deprecated their Auto-Minify feature as of 2025. Consider using WordPress plugins or build tools for asset minification instead.
Cloudflare + Nginx Architecture
Visitor Request
Cloudflare Edge Network
CF-Connecting-IP Header Added
Nginx Receives Request
Real IP Extracted from Header
WordPress (Correct Visitor IP)
💡 Important: Regularly update the Cloudflare IP list to ensure proper functionality as Cloudflare updates their infrastructure.

Performance Optimization Summary

Caching Layer Hierarchy

Complete Caching Stack
CDN / Cloudflare (Edge Cache)
Browser Cache (Client-side)
Nginx FastCGI Cache (Server-side)
Page Cache Plugin (WP Super Cache / W3TC)
Redis Object Cache (Memory)
OPcache (PHP Bytecode)
MySQL Database Query Cache

Recommended Configuration Matrix

Site Type Page Cache Object Cache OPcache CDN
Static Site ✅ FastCGI or WP Super Cache ⚠️ Optional ✅ Required ✅ Highly Recommended
Blog Site ✅ W3TC with exceptions ✅ Redis ✅ Required ✅ Recommended
E-commerce ⚠️ Fragment caching only ✅ Redis (essential) ✅ Required ✅ For static assets
Membership Site ❌ Not recommended ✅ Redis (essential) ✅ Required ⚠️ Limited use

Best Practices Checklist

✅ Essential Optimizations:
  • 1 Enable OPcache on all servers
  • 2 Configure proper PHP-FPM process management
  • 3 Disable WordPress post revisions (or limit them)
  • 4 Use system cron instead of WP-Cron
  • 5 Implement appropriate caching for your site type
  • 6 Configure Cloudflare real IP forwarding
  • 7 Monitor PHP-FPM and cache logs regularly
  • 8 Keep Cloudflare IP list updated

Performance Testing

After implementing these optimizations, test your site performance using:

  • GTmetrix: Comprehensive performance analysis
  • Google PageSpeed Insights: Core Web Vitals and optimization suggestions
  • WebPageTest: Detailed waterfall analysis
  • Pingdom: Global performance testing
⚠️ Important Reminders:
  • Always backup your configuration files before making changes
  • Test configuration changes with sudo nginx -t before reloading
  • Monitor server resources after implementing caching solutions
  • Clear all caches when troubleshooting issues
  • Document your configuration changes for future reference