Table of Contents
- Overview
- Prerequisites
- Quick Architecture
- Install / Setup
- Base Configuration
- Reload/Enable & Health Checks
- Security / Hardening
- Performance & Optimization
- Backup & Restore
- Troubleshooting (Top issues)
- Key Takeaways & Next Steps
Overview — WordPress Nginx FastCGI Cache
wordpress nginx fastcgi cache php-fpm setup — This guide deploys WordPress on Nginx with FastCGI cache for big performance gains. We’ll set cookie-based cache bypass for logged-in users and comments, enable HTTPS, and add health checks and hardening. All configs are copy-pasteable with heredocs. Reference: Nginx documentation and Let’s Encrypt.
Prerequisites
- A Linux server with sudo and a public DNS name (DOMAIN).
- Open ports: 80 (HTTP), 443 (HTTPS).
- Placeholders to replace: DOMAIN, WEBROOT (e.g., /var/www/wordpress), PHP_SOCK (e.g., /run/php/php-fpm.sock).
- Distros covered: Ubuntu/Debian, RHEL-family (RHEL/Rocky/CentOS Stream/Fedora), Arch/Manjaro, openSUSE/SLE.
Quick Architecture
Client -> Nginx (TLS + FastCGI cache) -> PHP-FPM -> WordPress (files + DB)
^ bypass cache for logged-in/comments via cookies
Install / Setup — WordPress on Nginx (PHP-FPM + FastCGI Cache)
1) Install Nginx, PHP-FPM, and Certbot (for HTTPS).
Ubuntu/Debian
sudo apt update
sudo apt install -y nginx php-fpm php-mysql php-xml php-gd php-curl php-zip php-mbstring mariadb-client
sudo apt install -y certbot python3-certbot-nginx
RHEL-Family (RHEL/Rocky/CentOS Stream/Fedora)
sudo dnf -y install nginx php-fpm php-mysqlnd php-xml php-gd php-curl php-zip php-mbstring mariadb-connector-c
sudo dnf -y install certbot python3-certbot-nginx
# Enable PHP-FPM service name can vary (php-fpm):
sudo systemctl enable --now php-fpm
Arch / Manjaro
sudo pacman -Sy --noconfirm nginx php-fpm php-gd php-curl php-zip php-mysqli mariadb-clients certbot
sudo systemctl enable --now php-fpm
openSUSE / SLE
sudo zypper refresh
sudo zypper install -y nginx php8-fpm php8-mysql php8-xml php8-gd php8-curl php8-zip php8-mbstring mariadb-tools certbot python3-certbot-nginx
sudo systemctl enable --now php-fpm
2) Create the cache path and base tuning snippet.
# Cache path & map rules (one-time)
sudo tee /etc/nginx/conf.d/wordpress-cache.conf > /dev/null <<'NGINC'
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=WP:200m inactive=60m use_temp_path=off;
# Bypass cache for logged-in & commenters
map $http_cookie $skip_cache {
default 0;
"~*wordpress_logged_in_" 1;
"~*comment_author" 1;
"~*woocommerce_items_in_cart|woocommerce_cart_hash|wp-postpass_" 1;
}
# Bypass via query flag
map $arg_nocache $query_nocache {
default 0;
"1" 1;
"true" 1;
}
NGINC
sudo mkdir -p /var/cache/nginx/fastcgi && sudo chown -R www-data:www-data /var/cache/nginx/fastcgi 2>/dev/null || sudo chown -R nginx:nginx /var/cache/nginx/fastcgi 2>/dev/null || true
3) Obtain Let’s Encrypt certificates (auto HTTPS).
sudo certbot --nginx -d DOMAIN -d www.DOMAIN --redirect -m admin@DOMAIN --agree-tos --non-interactive
sudo systemctl enable --now certbot.timer 2>/dev/null || sudo systemctl enable --now certbot-renew.timer 2>/dev/null || true
Base Configuration
4) WordPress vhost with FastCGI cache and cookie-based bypass (copy as one block). Replace DOMAIN, WEBROOT, PHP_SOCK.
# /etc/nginx/conf.d/DOMAIN.conf
sudo tee /etc/nginx/conf.d/DOMAIN.conf > /dev/null <<'NGINX'
server {
listen 443 ssl http2;
listen 443 quic reuseport;
server_name DOMAIN www.DOMAIN;
root WEBROOT;
index index.php index.html;
ssl_certificate /etc/letsencrypt/live/DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/DOMAIN/privkey.pem;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
# Static
location ~* \.(?:css|js|jpg|jpeg|gif|png|webp|ico|svg|ttf|otf|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
# PHP front controller
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:PHP_SOCK;
# Cache key and rules
fastcgi_cache_key "$scheme$request_method$host$request_uri";
set $bypass 0;
if ($skip_cache) { set $bypass 1; }
if ($request_method = POST) { set $bypass 1; }
if ($query_nocache = 1) { set $bypass 1; }
fastcgi_no_cache $bypass;
fastcgi_cache_bypass $bypass;
fastcgi_cache WP;
fastcgi_cache_valid 200 301 302 1h;
add_header X-Cache $upstream_cache_status always;
}
# Deny access to sensitive files
location ~* /\.(ht|git) { deny all; }
}
server {
listen 80;
server_name DOMAIN www.DOMAIN;
return 301 https://$host$request_uri;
}
NGINX
sudo nginx -t && sudo systemctl reload nginx
5) PHP-FPM socket path quick reference per distro.
- Ubuntu/Debian: `/run/php/php-fpm.sock` or `/run/php/php8.1-fpm.sock` (check `ls /run/php/`)
- RHEL-Family: `/run/php-fpm/www.sock`
- Arch/Manjaro: `/run/php-fpm/php-fpm.sock`
- openSUSE/SLE: `/run/php-fpm/php-fpm.sock` or versioned
Reload/Enable & Health Checks
6) Health checks and quick validation.
sudo nginx -t && sudo systemctl reload nginx
systemctl --no-pager --full status php-fpm | sed -n '1,40p'
curl -I https://DOMAIN | sed -n '1,20p'
# Warm and test cache:
curl -I https://DOMAIN/ | grep -i x-cache
curl -I https://DOMAIN/?nocache=1 | grep -i x-cache
Security / Hardening
# Firewall examples (limit where possible)
sudo ufw allow 80/tcp 2>/dev/null || true
sudo ufw allow 443/tcp 2>/dev/null || true
sudo ufw allow 443/udp 2>/dev/null || true
sudo ufw enable 2>/dev/null || true
# systemd drop-in for php-fpm and nginx (optional hardening)
sudo mkdir -p /etc/systemd/system/nginx.service.d /etc/systemd/system/php-fpm.service.d 2>/dev/null || true
sudo tee /etc/systemd/system/nginx.service.d/override.conf > /dev/null <<'OVR'
[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
OVR
sudo tee /etc/systemd/system/php-fpm.service.d/override.conf > /dev/null <<'OVR2'
[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
OVR2
sudo systemctl daemon-reload && sudo systemctl restart nginx php-fpm || true
Performance & Optimization — for WordPress on Nginx
- Ensure OPcache is enabled in PHP for faster PHP execution.
- Cache only GET/HEAD; avoid caching POST or query-string heavy endpoints.
- Bypass cache for logged-in and cart sessions to prevent stale personalized content.
7) Optional: enable Brotli (if available) or keep gzip for text assets.
Backup & Restore
sudo tar czf wp-nginx-cache-backup-$(date +%F).tgz /etc/nginx/conf.d /etc/nginx/nginx.conf /var/www /etc/letsencrypt /var/cache/nginx/fastcgi
# Restore:
# sudo tar xzf wp-nginx-cache-backup-YYYY-MM-DD.tgz -C /
# sudo nginx -t && sudo systemctl reload nginx
Troubleshooting (Top issues)
1) X-Cache is always MISS — PHP socket path wrong or cache bypass matched.
grep -n 'fastcgi_pass' /etc/nginx/conf.d/DOMAIN.conf; ls -l /run/php* /run/php-fpm* 2>/dev/null || true
2) Logged-in pages get cached — check cookie rules in the map.
grep -n 'map $http_cookie' /etc/nginx/conf.d/wordpress-cache.conf; nginx -t
3) 502 Bad Gateway — PHP-FPM down or socket path wrong.
systemctl status php-fpm | sed -n '1,60p'
4) Certbot fails — DNS not propagated or port 80 blocked.
dig +short DOMAIN; curl -I http://DOMAIN | head -n1
Key Takeaways & Next Steps
- wordpress nginx fastcgi cache php-fpm setup best practices across major Linux distros.
- FastCGI cache significantly reduces PHP load and TTFB for anonymous traffic.
- Bypass rules protect logged-in/checkout flows; add nocache flag for debugging.
- Next: add cache purge hooks via deploy webhooks or wp-cli after content updates.
