WordPress Performance Optimization Guide

NGINX Configuration & Advanced Caching Solutions

Introduction to WordPress Caching Strategies

Understanding Caching Architecture

With WordPress sites, we need to differentiate between static and dynamic content delivery. This guide covers comprehensive server-side caching solutions and caching plugins to optimize both static and dynamic WordPress sites.

Caching Efficiency: NGINX vs PHP

The efficiency of caching fundamentally depends on where cached pages are served. The most optimal approach involves NGINX serving static cache pages directly without invoking PHP. While serving cached pages through PHP provides benefits, it adds more overhead compared to direct file serving by NGINX.

Caching Performance Comparison

Client Request
NGINX
(Direct Cache Serving)
⚡ Fastest
Response
Client Request
NGINX
PHP-FPM
(Cache via PHP)
⚠ Slower
Response

Recommended Caching Solutions

Solution Type Best For Features
FastCGI Caching Server-side Static Sites Maximum efficiency, direct NGINX serving, requires additional plugins for object caching
WP Super Cache Plugin Static Sites Easy to use, generates static HTML pages, NGINX serves directly
W3 Total Cache Plugin Dynamic Sites Comprehensive: page cache, browser cache, object cache, CDN integration
Redis Object Cache Dynamic Sites In-memory data store, reduces database load, speeds up data retrieval

⚠ Critical Warning: Do NOT Combine Multiple Caching Solutions!

Never implement multiple caching solutions on the same site. Choose ONE solution from the following options:

  • FastCGI Caching OR
  • WP Super Cache OR
  • W3 Total Cache

Why?

  • Complexity & Conflicts: Different caching mechanisms may have conflicting settings or strategies, causing unpredictable behavior or errors
  • Resource Overhead: Multiple caching solutions create redundant processes that consume server resources unnecessarily
  • Compatibility Issues: Caching plugins may interfere with each other's cache keys or mechanisms
  • Performance Degradation: Instead of improving performance, multiple caching layers can actually slow down your site

Static vs Dynamic WordPress Sites

Static WordPress Sites

For static sites, the emphasis is on implementing page caching solutions. Since static sites don't typically involve dynamic content requiring object caching, the primary goal is to cache entire pages for quick and efficient delivery. This approach reduces server load and improves site responsiveness.

Static Site Strategy: Focus on FastCGI caching OR WP Super Cache OR W3 Total Cache for page delivery optimization.

Dynamic WordPress Sites

Dynamic sites require optimization through both page caching and object caching. This approach involves using plugins like WP Super Cache or W3 Total Cache for page caching, plus configuring object caching (Redis) for storing database query results and dynamic data in memory.

Dynamic Site Strategy: Implement page caching (WP Super Cache OR W3 Total Cache) + Redis for object caching. A dedicated Redis plugin ensures seamless integration between WordPress and Redis.

Important Note on Uninstallation

This guide covers both installation/configuration and proper uninstallation procedures for each caching solution. If you decide to switch caching solutions, follow the correct uninstallation procedure to completely remove the previous solution before implementing a new one.

1. Disable Post Revisions

WordPress automatically saves post revisions, which can clutter your database. Disabling post revisions helps reduce database size and improve performance.

Step 1: Navigate to WordPress directory
cd /var/www/example.com/
Step 2: Edit wp-config.php
sudo nano public_html/wp-config.php

Add this line to wp-config.php:

define('WP_POST_REVISIONS', 'false');
Step 3: Restart PHP-FPM (adjust version as needed)
sudo systemctl restart php8.3-fpm

Note: Cloudflare has deprecated the Auto Minify feature. Reference: Cloudflare Community

2. Configure Memory Limits

Proper memory allocation is crucial for WordPress performance. This section covers configuring both PHP-FPM pool memory and WordPress memory limits.

PHP-FPM Pool Memory Configuration

Edit your PHP-FPM pool configuration file:

; Disable restrictive 32M limit
;php_admin_value[memory_limit] = 32M

; Set appropriate memory limit
php_admin_value[memory_limit] = 256M

WordPress Memory Limit

Add to wp-config.php:

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

Best Practice: Set memory_limit to 256M for most WordPress installations. Adjust based on your site's specific requirements and available server resources.

3. Configure WP-Cron

WordPress uses WP-Cron to schedule tasks, but it's triggered by page loads, which can be inefficient. Disabling WP-Cron and using system cron provides better control and reliability.

Step 1: Disable WordPress WP-Cron

Add to wp-config.php:

define('DISABLE_WP_CRON', true);
Restart PHP-FPM
sudo systemctl restart php8.1-fpm

Step 2: Setup System Cron Job

Edit crontab
crontab -e

Add this 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 vs System Cron

Default WP-Cron
Triggered by page visits
Unreliable timing
VS
System Cron
Scheduled execution
Reliable & efficient

4. OPcache Configuration

OPcache improves PHP performance by storing precompiled script bytecode in memory, eliminating the need to load and parse scripts on each request. Proper configuration differs between development and production environments.

Locate Your PHP-FPM Pool Configuration

Navigate to pool directory
cd /etc/php/8.3/fpm/pool.d/
List pool configuration files
ls
Edit your site's pool configuration
sudo nano example.com.conf

Development Server Configuration

OPcache settings for development (July 2025 recommended):

; 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

Development Settings Explanation:

  • validate_timestamps = 1: OPcache checks for file changes
  • revalidate_freq = 2: Checks for changes every 2 seconds
  • These settings allow you to see code changes immediately during development

Production Server Configuration

OPcache settings for production (July 2025 recommended):

; 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

Production Settings Explanation:

  • validate_timestamps = 0: Disables timestamp checking for maximum performance
  • OPcache never checks if files have changed, serving cached bytecode indefinitely
  • After code updates, you must manually reload PHP-FPM to clear the cache
Reload PHP-FPM to apply changes
sudo systemctl reload php8.3-fpm

Calculate Optimal max_accelerated_files

The max_accelerated_files directive controls the maximum number of PHP files OPcache can store. Set this value higher than your total PHP file count.

Count total PHP files in your web directory
cd /var/www/
sudo find . -type f -print | grep php | wc -l

Recommended Values:

  • Small sites (< 3,000 files): 4000
  • Medium sites (3,000-10,000 files): 10000
  • Large sites (> 10,000 files): 20000+

Reference: PHP Manual - OPcache Configuration

5. FastCGI Caching Configuration

FastCGI caching is one of the most efficient caching methods available. NGINX serves cached content directly from disk without invoking PHP, resulting in exceptional performance and minimal server load.

FastCGI Caching Architecture

Client Request
NGINX
Check Cache
Cache HIT
Serve directly
⚡ Ultra Fast
Client Request
NGINX
Check Cache
Cache MISS
PHP-FPM
MySQL
Generate & Cache

Step 1: Configure NGINX HTTP Context

Edit main NGINX configuration
cd /etc/nginx
sudo nano nginx.conf

Add these directives in the HTTP context (above "# Virtual Host Configs"):

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

Directive Explanations:

  • fastcgi_cache_path: Defines cache location, size, and expiration. Use unique NAME for each site
  • fastcgi_cache_key: Defines how cache keys are generated
  • fastcgi_cache_use_stale: Serves stale cache if backend is unavailable
  • fastcgi_ignore_headers: Ignores certain headers that might prevent caching
  • fastcgi_cache_valid: Cache lifetime (60m = 60 minutes)

Step 2: Create Cache Exclusion Rules

Create includes directory if it doesn't exist
cd /etc/nginx/includes/
sudo nano fastcgi_cache_excludes.conf

Cache exclusion configuration:

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

Step 3: Configure Server Block

Edit your site's server block configuration
cd /etc/nginx/sites-available
sudo nano example.com.conf

Add these directives to your server block:

# Include cache exclusion rules
include /etc/nginx/includes/fastcgi_cache_excludes.conf;

# Add cache status header (useful for debugging)
add_header X-FastCGI-Cache $upstream_cache_status;

Step 4: Test and Reload NGINX

Test NGINX configuration
sudo nginx -t
Reload NGINX
sudo systemctl reload nginx

Step 5: Verify Caching is Working

Test HTTP headers (non-www)
curl -I https://example.com
Test HTTP headers (www)
curl -I https://www.example.com

Look for the X-FastCGI-Cache header:

  • MISS: Cache was not found, content generated by PHP
  • HIT: Content served from cache
  • BYPASS: Cache deliberately skipped (admin pages, logged in users, etc.)

Step 6: Setup Cache Purging (Optional)

Add a purge location to manually clear specific cached pages when needed.

Add this location block to your server configuration:

location ~ /purge(/.*) {
    fastcgi_cache_purge NAME "$scheme$request_method$host$1";
}

Usage: Visit https://example.com/purge/page-url to purge a specific cached page. Replace "NAME" with your actual cache zone name.

Removing FastCGI Caching

Complete Uninstallation Steps

Step 1: Edit server block configuration
cd /etc/nginx/sites-available
sudo nano example.com.conf

Remove or comment out these directives:

# Remove the cache exclusions include
# include /etc/nginx/includes/fastcgi_cache_excludes.conf;

# Comment out FastCGI caching directives in the PHP location block
# fastcgi_cache_bypass $skip_cache;
# fastcgi_no_cache $skip_cache;
# fastcgi_cache NAME;
# fastcgi_cache_valid 60m;

# Remove cache status header
# add_header X-FastCGI-Cache $upstream_cache_status;

# Remove purge location block
# location ~ /purge(/.*) {
#     fastcgi_cache_purge NAME "$scheme$request_method$host$1";
# }
Step 2: Test and reload NGINX
sudo nginx -t
sudo systemctl reload nginx
Step 3: Restart PHP-FPM
sudo systemctl restart php8.1-fpm

Note: The fastcgi_cache directives can remain in nginx.conf as they are only activated when called from server blocks.

6. WP Super Cache Configuration

WP Super Cache generates static HTML files from your dynamic WordPress content. When properly configured with NGINX, these static files are served directly, bypassing PHP entirely for exceptional performance.

Step 1: Create Cache Exclusion Rules

Create WP Super Cache exclusion file
cd /etc/nginx/includes/
sudo nano wp_super_cache_excludes.conf

WP Super Cache NGINX exclusion rules:

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

How it works: The try_files directive first looks for cached HTML files in the WP Super Cache directory. If found, NGINX serves them directly. If not found, the request is passed to PHP.

Step 2: Configure Server Block

Edit your site's configuration
cd /etc/nginx/sites-available/
sudo nano example.com.conf

Include the exclusion rules:

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

Step 3: Install and Configure WP Super Cache Plugin

  1. Install WP Super Cache plugin from WordPress dashboard
  2. Activate the plugin
  3. Go to Settings → WP Super Cache
  4. Select "Use mod_rewrite to serve cache files" (this works with NGINX)
  5. Configure cache settings according to your requirements

Uninstalling WP Super Cache

Step 1: Edit server block
cd /etc/nginx/sites-available/
sudo nano example.com.conf

Remove the include directive:

# REMOVE:
# include /etc/nginx/includes/wp_super_cache_exclusions.conf;

# UNCOMMENT the default location block:
location / {
    try_files $uri $uri/ /index.php$is_args$args;
}
Step 2: Test and reload services
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl reload php8.3-fpm
  1. Deactivate and delete WP Super Cache plugin from WordPress dashboard
  2. Verify cache directories are removed from wp-content/cache/

7. W3 Total Cache Configuration

W3 Total Cache is a comprehensive caching solution that handles page caching, object caching, browser caching, and CDN integration. It's particularly well-suited for dynamic WordPress sites.

Step 1: Prepare nginx.conf File

W3 Total Cache generates its own NGINX configuration rules. We need to create a file that W3TC can write to.

Navigate to WordPress root
cd /var/www/example.com/public_html/
Create nginx.conf file
sudo touch nginx.conf
Set ownership (replace username with your actual username)
sudo chown username:username nginx.conf
Set permissions
sudo chmod 660 nginx.conf
Verify permissions
sudo ls -l public_html/

Step 2: Secure nginx.conf

Prevent direct access to nginx.conf via web browser for security.

Edit security directives
cd /etc/nginx/includes/
sudo nano nginx_security_directives.conf

Add this line:

location = /nginx.conf { deny all; }

Step 3: Create W3TC Cache Exclusion Rules

Create exclusion file
cd /etc/nginx/includes/
sudo nano w3tc_cache_excludes.conf

W3 Total Cache exclusion configuration:

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

Step 4: Configure Server Block

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

Comment out default location block and add includes:

# Comment out the default location block
#location / {
#    try_files $uri $uri/ /index.php$is_args$args;
#}

# Add W3TC includes
include /etc/nginx/includes/w3tc_cache_excludes.conf;
include /var/www/example.com/public_html/nginx.conf;
Test and reload NGINX
sudo nginx -t
sudo systemctl reload nginx

Step 5: Install PHP Tidy Extension (Required)

W3 Total Cache requires the PHP Tidy extension for HTML minification.

Update package list
sudo apt update
Install php-tidy
sudo apt install php8.3-tidy
Reload services
sudo systemctl reload nginx
sudo systemctl reload php8.3-fpm

Step 6: Install and Configure W3 Total Cache

  1. Install W3 Total Cache plugin from WordPress dashboard
  2. Activate the plugin
  3. Go to Performance → General Settings
  4. Enable Page Cache and select "Disk: Enhanced" method
  5. Configure other settings as needed (Minify, Database Cache, Object Cache, etc.)
  6. Save all settings

Uninstalling W3 Total Cache

Step 1: Edit server block
cd /etc/nginx/sites-available/
sudo nano example.com.conf

Restore default location block and remove includes:

# RESTORE default location block (uncomment):
location / {
    try_files $uri $uri/ /index.php$is_args$args;
}

# REMOVE these includes:
# include /etc/nginx/includes/w3tc_cache_excludes.conf;
# include /var/www/example.com/public_html/nginx.conf;
Step 2: Remove W3TC cache directories
cd /var/www/example.com/public_html/wp-content/
sudo rm -rf cache/ w3tc-config/
Step 3: Remove nginx.conf file
cd /var/www/example.com/public_html/
sudo rm nginx.conf
Step 4: Reload services
sudo systemctl reload php8.3-fpm
sudo systemctl reload nginx
  1. Deactivate and delete W3 Total Cache plugin from WordPress dashboard

8. Redis Object Caching

Redis is an in-memory data structure store used as a database cache. It significantly reduces database load by storing frequently accessed data in memory, resulting in faster data retrieval and improved performance for dynamic WordPress sites.

Redis Object Caching Flow

WordPress
Request Data
Redis Cache
In-Memory Store
⚡ Fast
MySQL
Only on Cache Miss

Step 1: Install Redis Server and PHP Extension

Update package list
sudo apt update
Install Redis server and PHP Redis extension
sudo apt install redis-server php8.3-redis

Step 2: Verify Redis Installation

Check Redis server status
sudo systemctl status redis-server
Check Redis log for errors
sudo cat /var/log/redis/redis-server.log

Step 3: Fix Memory Overcommit Warning

You may see this warning in the log:

WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.

Create sysctl configuration for Redis
cd /etc/sysctl.d/
sudo nano 11-redis.conf

Add this line:

vm.overcommit_memory = 1
Reboot to apply changes
sudo reboot
After reboot, verify Redis status and logs
sudo systemctl status redis-server
sudo cat /var/log/redis/redis-server.log

Step 4: Configure Redis Memory and Eviction Policy

Edit Redis configuration
cd /etc/
sudo ls redis/
sudo nano redis/redis.conf

Add or modify these lines:

maxmemory 256mb
maxmemory-policy allkeys-lru

Configuration Explanation:

  • maxmemory 256mb: Limits Redis memory usage to 256MB
  • maxmemory-policy allkeys-lru: When memory limit is reached, Redis removes the least recently used keys
Restart Redis and reboot
sudo systemctl restart redis-server
sudo reboot

Step 5: Configure WordPress for Redis

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

Add this line (replace example.com with your domain):

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

Note: The WP_CACHE_KEY_SALT ensures that cache keys are unique to your site, preventing conflicts if multiple WordPress sites use the same Redis instance.

Step 6: Install Redis Object Cache Plugin

  1. Install "Redis Object Cache" plugin from WordPress dashboard
  2. Activate the plugin
  3. Go to Settings → Redis
  4. Click "Enable Object Cache"
  5. Verify connection status shows "Connected"

WooCommerce-Specific Configuration

If you're running WooCommerce, you need to prevent Redis from caching session data to avoid cart issues.

URLs to Exclude from Cache:

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

Cookies to Exclude:

  • woocommerce_cart_hash
  • woocommerce_items_in_cart
  • wp_woocommerce_session_
  • woocommerce_recently_viewed
  • store_notice[notice id]

Add to wp-config.php to prevent Redis from caching WooCommerce sessions:

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

9. PHP-FPM Optimization

Proper PHP-FPM pool configuration is essential for optimal performance. The ondemand process manager is particularly efficient for servers hosting multiple sites.

Calculate Optimal PHP-FPM Settings

Step 1: Display All Pool Usernames

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

Step 2: Calculate Average Memory Per Process

Replace POOL_USER with 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") }'

Example Calculation:

If average memory per process = 40M and available RAM = 2GB (2048MB):

max_children = 2048MB / 40MB = 51 processes

Set max_children to 51 (or slightly less for safety margin).

Configure OnDemand Process Manager

Edit pool configuration
cd /etc/php/8.1/fpm/pool.d/
ls
sudo nano example.com.conf

OnDemand configuration:

pm = ondemand
pm.max_children = [as calculated above]
pm.process_idle_timeout = 10s
pm.max_requests = 500

OnDemand Settings Explanation:

  • pm = ondemand: Processes are spawned on demand and terminated when idle
  • pm.max_children: Maximum number of child processes (calculated based on available memory)
  • pm.process_idle_timeout: Time before idle processes are killed (10 seconds)
  • pm.max_requests: Number of requests each child process handles before respawning
Reload PHP-FPM
sudo systemctl reload php8.3-fpm

Monitor for max_children Issues

Check PHP-FPM logs for warnings
ls /var/log/
sudo grep max_children /var/log/php8.3-fpm.log

If you see this warning, increase max_children value:

WARNING: [pool www] server reached max_children setting (25), consider raising it

10. Cloudflare IP Configuration

When using Cloudflare as a CDN/proxy, NGINX sees Cloudflare's IP addresses instead of visitors' real IPs. Configuring NGINX to trust Cloudflare's IPs ensures accurate logging and functionality.

Why This Configuration is Necessary

Request Flow with Cloudflare

Visitor
Real IP: 203.0.113.45
Cloudflare
Proxy IP
Your Server
Sees Cloudflare IP

Cloudflare passes the real visitor IP in the CF-Connecting-IP header. We need to configure NGINX to read this header and trust Cloudflare's IP ranges.

Step 1: Get Current Cloudflare IP Ranges

Official Cloudflare IP Lists:

Always verify you're using the most current IP ranges from Cloudflare's official documentation.

Step 2: Create Cloudflare IP Configuration

Create Cloudflare IP list file
cd /etc/nginx/includes
sudo nano cloudflare_ip_list.conf

Cloudflare IP configuration (Last updated October 2022):

# Last updated OCT 2022
# IPv4 Addresses
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;

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

# Tell NGINX to use CF-Connecting-IP header
real_ip_header CF-Connecting-IP;

Important: Keep IP Ranges Updated

Cloudflare occasionally updates their IP ranges. Periodically check the official Cloudflare IP lists and update your configuration accordingly. Using outdated IP ranges may result in NGINX not properly identifying visitor IPs.

Step 3: Include Configuration in Server Block

Edit your site's server block
cd /etc/nginx/sites-available/
sudo nano example.com.conf

Add this include directive in the server block:

include /etc/nginx/includes/cloudflare_ip_list.conf;
Test and reload NGINX
sudo nginx -t
sudo systemctl reload nginx

Verify Configuration

After configuration, check your NGINX access logs. You should now see real visitor IP addresses instead of Cloudflare's IPs.

Benefits:

  • Accurate visitor IP logging
  • Proper geolocation functionality
  • Correct rate limiting and security rules
  • Accurate analytics and reporting

Summary & Best Practices

Key Takeaways

  • Choose ONE caching solution: Never combine FastCGI, WP Super Cache, and W3 Total Cache
  • Static sites: FastCGI OR WP Super Cache OR W3 Total Cache for page caching
  • Dynamic sites: Page caching (WP Super Cache or W3TC) + Redis for object caching
  • Always test: Use curl -I to verify caching headers after configuration
  • Monitor performance: Check PHP-FPM logs and adjust max_children as needed
  • Keep updated: Regularly update Cloudflare IP ranges and PHP versions

Performance Optimization Checklist

Component Action Impact
Post Revisions Disable in wp-config.php Reduces database size
Memory Limits Set to 256M Prevents memory exhaustion
WP-Cron Use system cron instead Reliable scheduled tasks
OPcache Configure based on environment Faster PHP execution
Page Cache Implement ONE solution Dramatically faster page loads
Object Cache (Dynamic) Use Redis Reduces database queries
PHP-FPM Use ondemand, calculate max_children Efficient resource usage
Cloudflare IPs Configure real_ip_from Accurate visitor tracking

Final Notes

This guide provides comprehensive server-side optimization for WordPress sites using NGINX. Always test configuration changes in a staging environment before applying to production. Monitor server logs regularly to identify and resolve issues early.

Remember that optimization is an ongoing process. As your site grows and traffic patterns change, revisit these configurations and adjust them accordingly.