Nginx FastCGI Cache on Magento 2 with PHP-FPM
Configuring Nginx FastCGI Cache on Magento 2 is one of the most effective ways to improve your store’s page speed and reduce server load. This guide covers the complete Nginx FastCGI Cache Magento 2 setup with PHP-FPM — no additional software or reverse proxy required. FastCGI cache is built directly into Nginx and stores the HTML output generated by PHP, serving repeat requests instantly without invoking PHP or querying the database.
Without Nginx FastCGI Cache Browser → Nginx → PHP-FPM → Magento 2 → Database every request runs PHP and hits the database – slow under load With Nginx FastCGI Cache (HIT) Browser → Nginx → FastCGI Cache → Response zero PHP, zero database – instant response for every guest visitor
Prerequisites
Before setting up Nginx FastCGI Cache on Magento 2, ensure the following are in place on your server:
- Ubuntu 20.04 / 22.04 / 24.04
- Nginx 1.18 or higher
- PHP 8.1 / 8.2 / 8.3 / 8.4 with PHP-FPM installed
- Magento 2.4.x installed and running
- Root or sudo access to the server
Configure PHP-FPM Pool
PHP-FPM runs as a separate process that Nginx communicates with. First, install PHP-FPM with all extensions required by Magento 2:
sudo apt update
sudo apt install php8.3-fpm php8.3-mysql php8.3-xml php8.3-curl \
php8.3-gd php8.3-mbstring php8.3-intl php8.3-bcmath \
php8.3-soap php8.3-zip php8.3-xsl -y
Create a dedicated Magento PHP-FPM pool configuration:
sudo nano /etc/php/8.3/fpm/pool.d/magento.conf
[magento] user = www-data group = www-data ; TCP socket — matches the Nginx upstream directive listen = 127.0.0.1:9000 pm = dynamic pm.max_children = 20 pm.start_servers = 5 pm.min_spare_servers = 3 pm.max_spare_servers = 10 pm.max_requests = 500 php_admin_value[memory_limit] = 756M php_admin_value[max_execution_time] = 600 php_admin_value[upload_max_filesize] = 64M php_admin_value[post_max_size] = 64M php_admin_flag[log_errors] = on ; OPcache — strongly recommended for Magento 2 performance php_admin_value[opcache.memory_consumption] = 512 php_admin_value[opcache.max_accelerated_files] = 60000 ; Sessions php_value[session.save_handler] = files php_value[session.save_path] = /var/www/html/magento/var/session
Enable and start PHP-FPM:
sudo systemctl enable php8.3-fpm sudo systemctl start php8.3-fpm
Configure Nginx FastCGI Cache Zone
The Nginx FastCGI Cache zone is defined inside the http { } block of /etc/nginx/nginx.conf. This allocates shared memory for cache keys and sets the disk path where cached responses are stored.
sudo nano /etc/nginx/nginx.conf
Add the following inside the http { } block:
# ── Nginx FastCGI Cache Zone for Magento 2 ───────────────────────────
# keys_zone=magento_cache:256m → 256MB RAM for cache index
# inactive=1d → remove cache unused for 1 day
# max_size=10g → max 10GB disk cache
fastcgi_cache_path /var/cache/nginx/magento_fastcgi_cache
levels=1:2
keys_zone=magento_cache:256m
inactive=1d
max_size=10g;
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;
; Buffer sizes — required for Magento 2 large response headers
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
Create the cache directory and set permissions:
sudo mkdir -p /var/cache/nginx/magento_fastcgi_cache sudo chown -R www-data:www-data /var/cache/nginx
Configure Magento 2 Nginx Server Block
Create the Nginx server block for Magento 2. This file defines the FastCGI cache bypass rules, static file handling, and the PHP-FPM upstream connection.
sudo nano /etc/nginx/conf.d/magento.conf
upstream fastcgi_backend {
server 127.0.0.1:9000;
keepalive 32;
}
server {
listen 80;
server_name yourdomain.com;
set $MAGE_ROOT /var/www/html/magento;
root $MAGE_ROOT/pub;
index index.php;
autoindex off;
charset UTF-8;
# ── FastCGI Cache Bypass Rules ────────────────────────────────────
set $no_cache 0;
# Never cache POST requests
if ($request_method = POST) { set $no_cache 1; }
# Never cache URLs with query strings
if ($query_string != "") { set $no_cache 1; }
# Never cache admin, checkout, cart, account, or API pages
if ($request_uri ~* "/(admin|adminhtml|checkout|cart|account|rest|graphql)") {
set $no_cache 1;
}
# Never cache requests from logged-in users
if ($http_cookie ~* "PHPSESSID|admin_user|customer_logged_in|adminhtml") {
set $no_cache 1;
}
# ── Security Headers ──────────────────────────────────────────────
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# ── Static Files — served by Nginx directly (no PHP needed) ──────
location /static/ {
expires max;
add_header Cache-Control "public";
location ~ ^/static/version\d*/ {
rewrite ^/static/version\d*/(.*)$ /static/$1 last;
}
if (!-f $request_filename) {
rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last;
}
}
location /media/ {
try_files $uri $uri/ /get.php$is_args$args;
}
# ── PHP-FPM with Nginx FastCGI Cache ─────────────────────────────
location ~ ^/(index|get|static|errors/report|errors/404|errors/503|print)\.php$ {
try_files $uri =404;
fastcgi_pass fastcgi_backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PHP_VALUE "memory_limit=756M \n max_execution_time=600";
fastcgi_read_timeout 600s;
fastcgi_connect_timeout 600s;
include fastcgi_params;
# Nginx FastCGI Cache directives
fastcgi_cache magento_cache;
fastcgi_cache_valid 200 301 302 1h; # cache hits for 1 hour
fastcgi_cache_valid 404 1m; # cache 404s for 1 minute
fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;
# Shows HIT / MISS / BYPASS in response headers
add_header X-FastCGI-Cache $upstream_cache_status;
}
# ── Magento 2 Front Controller ────────────────────────────────────
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# ── Deny Sensitive Files ──────────────────────────────────────────
location ~* (\.php$|\.htaccess$|\.git) { deny all; }
location ~ /\. { deny all; }
}
Set File Permissions
Correct file permissions allow Magento 2 to write cache, session, and generated files without errors:
# Set ownership
sudo chown -R www-data:www-data /var/www/html/magento
# Magento 2 writable directories
sudo chmod -R 775 /var/www/html/magento/var \
/var/www/html/magento/generated \
/var/www/html/magento/pub/static \
/var/www/html/magento/pub/media \
/var/www/html/magento/app/etc
# Make Magento CLI executable
sudo chmod +x /var/www/html/magento/bin/magento
Test the Configuration
Validate and Restart Services
sudo nginx -t sudo systemctl restart nginx sudo systemctl restart php8.3-fpm
A successful validation shows:
nginx: configuration file /etc/nginx/nginx.conf test is successful
Verify Nginx FastCGI Cache is Working
Use curl to check the X-FastCGI-Cache response header:
# First request — cache is empty curl -I http://yourdomain.com | grep X-FastCGI-Cache # X-FastCGI-Cache: MISS # Second request — served from Nginx FastCGI Cache curl -I http://yourdomain.com | grep X-FastCGI-Cache # X-FastCGI-Cache: HIT ⚡
Note: X-FastCGI-Cache: HIT confirms that Nginx FastCGI Cache on Magento 2 is working correctly — PHP-FPM and the database were not involved in serving the response.
Confirm Cache Files on Disk
ls -la /var/cache/nginx/magento_fastcgi_cache/
Each file in this directory represents one cached Magento 2 page response stored on disk.
Flush the Nginx FastCGI Cache
There are several ways to flush the Nginx FastCGI Cache on your Magento 2 store.
Full Flush — Delete All Cache Files
sudo rm -rf /var/cache/nginx/magento_fastcgi_cache/*
Flush Magento 2 + Nginx FastCGI Cache Together
php /var/www/html/magento/bin/magento cache:flush sudo rm -rf /var/cache/nginx/magento_fastcgi_cache/*
Reusable Flush Script
Save as flush-cache.sh and run it after every deployment:
#!/bin/bash echo "Flushing Magento 2 cache..." php /var/www/html/magento/bin/magento cache:flush echo "Flushing Nginx FastCGI Cache..." sudo rm -rf /var/cache/nginx/magento_fastcgi_cache/* echo "✅ All caches flushed successfully!"
chmod +x flush-cache.sh ./flush-cache.sh
Important: Always flush both the Magento 2 cache and the Nginx FastCGI Cache after deploying code changes, running setup:upgrade, or deploying static content. Stale cache can cause your visitors to see outdated pages.
Nginx FastCGI Cache Status Reference
Every Nginx response includes an X-FastCGI-Cache header. Here is what each status value means for your Magento 2 store:
| Status | Meaning | PHP Called? | DB Called? |
|---|---|---|---|
| HIT | Served directly from Nginx FastCGI Cache ⚡ | ❌ No | ❌ No |
| MISS | Cache was empty — PHP processed and stored the response | ✅ Yes | ✅ Yes |
| BYPASS | Cache skipped — admin, checkout, cart, or logged-in user | ✅ Yes | ✅ Yes |
| EXPIRED | Cached entry expired and is being refreshed by PHP-FPM | ✅ Yes | ✅ Yes |
What Nginx FastCGI Cache Serves on Magento 2
Homepage HIT Guest visitor, no session cookie Category pages HIT Guest visitor, no session cookie Product pages HIT Guest visitor, no session cookie CMS pages HIT Guest visitor, no session cookie Admin panel BYPASS URL rule — /admin Checkout / Cart BYPASS URL rule — /checkout Customer account BYPASS Session cookie detected REST API / GraphQL BYPASS URL rule — /rest, /graphql
Conclusion
Configuring Nginx FastCGI Cache on Magento 2 with PHP-FPM is one of the simplest and most impactful performance improvements for any Magento 2 store. The setup requires no additional software — FastCGI cache is built directly into Nginx — and dramatically reduces server load and response times under real traffic.
With Nginx FastCGI Cache in place, guest-facing pages such as the homepage, category pages, and product pages are served with zero PHP execution and zero database queries. At the same time, bypass rules ensure that the admin panel, checkout, cart, and logged-in customer sessions are always processed fresh by PHP-FPM, maintaining full Magento 2 functionality.
For further Magento 2 performance improvements, consider combining Nginx FastCGI Cache with Redis for session and cache storage, and OpenSearch for catalog search.