Skip to content

AS213941 · Peering-20% on all VPS with code NVH20 · Delivered in 60 seconds

Linux VPSFrom 2,49 €/monthWindows VPSFrom 3,49 €/monthFiveM serverFrom 0,99 €/monthMinecraft serverFrom 1,99 €/month
VPS & Cloud

Ranges

Linux NVMe VPSRoot access, Debian, Ubuntu, Alma…Windows RDP VPSRemote desktop, licence includedDedicated serversIntel Xeon Gold, exclusive resourcesPterodactyl VPSGame panel preinstalled

Network and hosting

Anti-DDoSWeb hostingColocationIP transitVPN

Professionals

Reseller VPSReseller programme
Solutions

Bots and automation

AI agents VPSA server for your coding assistants and autonomous agentsTelegram bot VPSPython or Node.js, always reachableDiscord bot VPSMusic, moderation, tickets: no downtimen8n VPSWorkflows with no execution limitVPN VPSAn encrypted tunnel exiting in FranceWeb scraping VPSScheduled data collection, dedicated IP

Trading

Forex VPSYour robots manage positions overnightMetaTrader 4 VPSSeveral MT4 terminals on one machineMetaTrader 5 VPSForex, indices and stocks in MT5NinjaTrader VPSFutures and NinjaScript strategiesTrading bot VPSOpen-source crypto bots, protected API keys

Windows desktop

Windows 10 VPSFor software that requires Windows 10Windows 11 VPSA modern Windows desktop, reachable anywhereSEO VPSLarge-site crawls without tying up your PCStreaming VPSAn OBS studio streaming from the data centreWordPress VPSFast websites on NVMe disks
Marketplace

Intelligence artificielle

Claude CodeL'agent de code d'Anthropic, dans votre terminalCodex CLIL'agent de code d'OpenAI en ligne de commandeOllamaDes modèles de langage locaux, sans API externeOpen WebUIUne interface de discussion pour vos modèles Ollama

Automatisation

n8nVos automatisations, sans limite d'exécutions

Déploiement et conteneurs

DockerLe moteur de conteneurs et Docker ComposeCoolifyDéployez vos applications depuis Git, sur votre serveurPortainerGérez vos conteneurs Docker depuis le navigateur

Supervision

Uptime KumaSurveillez vos sites et serveurs, alertes comprises

Network and security

WireGuardVotre propre VPN chiffré, rapide et moderneVaultwardenUn gestionnaire de mots de passe auto-hébergé

Sites et cloud

NextcloudVotre cloud de fichiers, agenda et contactsWordPressLe CMS le plus utilisé, sur une pile Nginx rapide

Jeux

PterodactylLe panel pour héberger vos serveurs de jeux
Gaming

Game servers

FiveM servertxAdmin, ESX, QBCoreMinecraft serverJava, Bedrock, modpacksRust serverOxide, Carbon, wipesNova-Life serverNova-Life: Amboise RP

Protection and tools

FiveM Anti-DDoSFree tools
Resources

Learn

DocumentationBlog

Assistance

ContactSLA commitment
View plans Client area Peering · AS213941

Installation

Optimising PHP-FPM, OPcache and the Nginx cache on a VPS

By the NVHCloud team · 2026-09-18 · 5 min read

On a PHP site, server response time comes down to three levels: does PHP compile the scripts on every request? Are there enough PHP processes for simultaneous visitors? And above all, do you need to run PHP for every page? This guide tunes all three, in that order. The examples target WordPress on Ubuntu 24.04 with PHP 8.3, but apply to Laravel or any other PHP application.

1. Measure before tuning

Without a baseline, you cannot tell whether a setting helped. Record the response time of a representative page, three times in a row:

for i in 1 2 3; do
  curl -o /dev/null -s -w "TTFB %{time_starttransfer}s\n" https://mysite.com/
done

Above 800 ms, Google considers the response time poor. Also note the figure for a page that cannot be cached, such as a shop's cart: that is the one PHP-FPM and Redis will improve. To interpret these values, see slow WordPress: finding the real cause (in French).

2. Enable and size OPcache

OPcache keeps already-compiled PHP code in memory. It is installed with PHP, but its default values are too small for WordPress and its plugins. Create an override file:

cat > /etc/php/8.3/fpm/conf.d/99-opcache.ini <<'EOF'
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
EOF
systemctl restart php8.3-fpm

With revalidate_freq=60, PHP checks at most once a minute whether a file has changed: a good compromise for a WordPress site updated from the admin panel.

⚠️ You will often see opcache.validate_timestamps=0 recommended. It is faster, but PHP no longer sees any file change until you reload PHP-FPM. Keep it for applications deployed by a script that runs systemctl reload php8.3-fpm on every release. On WordPress, a plugin update would not be picked up.

3. Size PHP-FPM

Each PHP request occupies a process. When they are all busy, the next visitors wait. The key setting is pm.max_children, the maximum number of processes. Too low, and the site queues; too high, and the server runs out of memory and becomes very slow.

First measure the average memory of a process while the site is running:

ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1; n++} END {printf "%.0f MB on average\n", s/n/1024}'

Then calculate: memory reserved for PHP ÷ memory per process. Example on a 4 GB VPS: keep about 1.5 GB for the system, Nginx and MariaDB, which leaves 2.5 GB for PHP. At 60 MB per process, that gives around forty; settle on 35 to keep a margin.

Set the pool in /etc/php/8.3/fpm/pool.d/www.conf:

pm = dynamic
pm.max_children = 35
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500

pm.max_requests recycles each process after 500 requests, which neutralises the small memory leaks of some plugins. On a 2 GB VPS with little traffic, pm = ondemand frees memory when the site is quiet.

To know whether the limit is reached, watch for this message in the log:

grep "max_children" /var/log/php8.3-fpm.log

If it appears regularly, raise the value if memory allows, otherwise move to a bigger plan.

4. Set up the FastCGI page cache

This is the setting with the biggest effect: Nginx keeps a copy of the generated HTML and serves it directly, without starting PHP or querying the database. A cached page responds in a few milliseconds.

Declare the cache zone in /etc/nginx/conf.d/fastcgi-cache.conf:

fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=WP:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout updating http_500 http_503;

Then, in the site's server block, define what must never be cached:

set $skip_cache 0;
if ($request_method = POST)       { set $skip_cache 1; }
if ($query_string != "")          { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-json/|/xmlrpc.php|wp-.*\.php|/feed/|sitemap") { set $skip_cache 1; }
if ($request_uri ~* "/panier/|/commande/|/mon-compte/|/cart/|/checkout/|/my-account/") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_logged_in|wp-postpass|woocommerce_items_in_cart|woocommerce_cart_hash") { set $skip_cache 1; }

And in the location ~ \.php$ block:

fastcgi_cache WP;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache $upstream_cache_status;
mkdir -p /var/cache/nginx/fastcgi
nginx -t && systemctl reload nginx
⚠️ The cookie rules are essential. Without them, a logged-in user's page, or a customer's cart, could be served from the cache to another visitor. Adapt the cart and account paths to those of your shop.

5. Clear the cache after a change

Nginx does not know that a post was edited: without purging, the old version is served until it expires, 60 minutes here. Two solutions:

  • Simple: keep a short lifetime and clear manually after an important change with rm -rf /var/cache/nginx/fastcgi/*.
  • Automatic: install the purge module (apt install libnginx-mod-http-cache-purge) and the WordPress plugin Nginx Helper, which purges the page concerned on every publication.

6. Add a Redis object cache

The page cache is useless on dynamic pages: cart, customer account, admin. For those, an object cache avoids repeating the same SQL queries from one page to the next.

apt install -y redis-server php8.3-redis
systemctl enable --now redis-server
systemctl restart php8.3-fpm

Then install the WordPress plugin Redis Object Cache and enable it in Settings → Redis. By default, Redis only listens on the local address: never expose it to the Internet.

7. Enable compression

Gzip compression is already enabled in Ubuntu's Nginx configuration, but only for HTML. Complete the http section of /etc/nginx/nginx.conf:

gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/css text/plain text/xml application/javascript application/json
           application/xml application/rss+xml image/svg+xml font/ttf;

There is no need to add JPEG, PNG or WebP images: they are already compressed.

8. Check the result

Request the same page twice and look at the header added above:

curl -sI https://mysite.com/ | grep -i x-cache
curl -sI https://mysite.com/ | grep -i x-cache

The first response shows MISS, the second HIT. Then rerun the measurement from step 1: on a cached page, the response time should drop below 100 ms from France. On dynamic pages, OPcache, PHP-FPM and Redis make the difference.

💡 If the response time stays high despite all this, the server probably lacks resources. See which VPS configuration for WordPress (in French) or our NVMe VPS.

Frequently asked questions

Do I need a caching plugin if I use the Nginx FastCGI cache?

No, not for the page cache: Nginx does it more efficiently than a plugin, since PHP does not start at all. A plugin is still useful for automatic purging, such as Nginx Helper, or for optimising CSS and JavaScript files. Avoid stacking two page caches; the results become unpredictable.

What value should I give pm.max_children?

Divide the memory you reserve for PHP by the average memory of a process, measured on your site. On a 4 GB VPS with processes of about 60 MB, a value around 35 is reasonable. There is no universal right value: it depends on your plugins.

Does the Nginx cache work with WooCommerce?

Yes, provided you exclude the cart, checkout, customer account and visitors with a cart in progress, as the rules in this guide do. Product and category pages stay cached for anonymous visitors, which takes a lot of load off the server.

Is Redis useful on a small brochure site?

Rarely. If almost all your pages are served from the Nginx cache, Redis adds almost nothing. It becomes useful as soon as a significant share of traffic is dynamic: shop, members' area, forum.

WordPress VPS

Your sites on NVMe, no limits, Anti-DDoS included.