PHP Performance Tuning in 2026: What Actually Makes a Difference

Most PHP speed gains in 2026 come from four things: run PHP 8.3 or 8.4, enable OPcache with enough memory, tune PHP-FPM worker counts to your CPU and RAM, and cut database queries per request. Micro-optimizations like single quotes barely register anymore.
Part of the complete web development guide.
On this page
- Know your PHP version before you touch anything
- OPcache is not optional
- About JIT: it’s not the free win people think
- PHP-FPM tuning is where a lot of sites are quietly broken
- Kill the N+1 queries
- Cache the expensive stuff, but cache it correctly
- Smaller things that add up
- Measure before and after, always
- FAQ
- Should I enable PHP’s JIT compiler?
- How much faster is PHP 8.4 than PHP 7.4?
- What’s the fastest single fix for a slow PHP site?
Most “PHP performance hack” lists are recycled from 2014 and still tell you to use single quotes and avoid regex. That advice won’t save you a millisecond on a modern stack. What actually matters in 2026 is how you configure OPcache and JIT, how you tune PHP-FPM, and how many times you hit the database per request.
I’ve spent years profiling PHP apps on everything from $5 VPS boxes to dedicated hardware, and the honest truth is that 90% of the gains come from a handful of things. Here’s what I’d check first.
Know your PHP version before you touch anything
If you’re still on PHP 7.4, stop reading optimization tips and upgrade. PHP 8.3 and 8.4 are the current supported releases going into 2026, with 8.4 being the one to target for new work. Each major version since 7.x has brought real runtime improvements, not marketing ones. PHP 8.0 alone was a meaningful jump over 7.4 in most real-world benchmarks, and 8.3/8.4 keep chipping away at it.
Check what you’re running:
php -v
php -m | grep -i opcache
If that returns nothing for OPcache, that’s your first problem. More on that below.
OPcache is not optional
Without OPcache, PHP parses and compiles your source files on every single request. That’s wasted CPU on files that never change between deploys. OPcache caches the compiled bytecode in shared memory so the next request skips straight to execution.
For production, these settings matter most:
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.jit=disable
opcache.jit_buffer_size=0
A few notes. validate_timestamps=0 tells PHP to stop checking whether files changed on disk. That’s a real win, but it means you must clear OPcache on every deploy. Do that as part of your deploy script, not by hand:
# after deploying new code
php -r "opcache_reset();"
# or if you're on FPM
systemctl reload php8.4-fpm
If you forget this step, you’ll deploy code and watch the old behavior stick around, which is a genuinely confusing afternoon. Wire it into CI so it can’t be forgotten.
About JIT: it’s not the free win people think
PHP’s JIT compiler gets hyped constantly, and for typical web apps it does less than the blog posts suggest. JIT helps most with CPU-heavy, long-running, math-and-loop style workloads. For a request that spends most of its time waiting on MySQL, Redis, or an HTTP call, JIT changes almost nothing.
My honest recommendation: leave JIT off for standard web apps and put that effort into fixing N+1 queries instead. If you have a specific CPU-bound hot path (image processing, heavy computation), benchmark with JIT on and off before assuming it helps. Measure, don’t guess.
PHP-FPM tuning is where a lot of sites are quietly broken
The default pm=dynamic config with tiny process counts falls over the moment traffic shows up. The right numbers depend on your RAM. Here’s the math that matters:
- Figure out roughly how much memory one PHP process uses (often 30-80MB depending on your app and extensions).
- Reserve memory for the OS, MySQL, Redis, and everything else on the box.
- Divide what’s left by per-process memory to get your realistic
pm.max_children.
A reasonable starting point for a box with, say, 8GB total and 4GB available to PHP:
; php-fpm pool config
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 8
pm.max_spare_servers = 20
pm.max_requests = 500
pm.max_requests recycles workers after a set number of requests, which helps contain memory leaks in long-lived processes. Set it too low and you pay process-spawn overhead constantly; too high and leaks accumulate. 500 is a sane middle ground to start from.
Kill the N+1 queries
This is the single biggest performance problem I find in real PHP codebases, and it has nothing to do with PHP itself. You load 100 orders, then loop and query the customer for each one. That’s 101 queries where 2 would do.
Bad:
$orders = $db->query("SELECT * FROM orders LIMIT 100");
foreach ($orders as $order) {
// one extra query per order
$customer = $db->query(
"SELECT * FROM customers WHERE id = ?",
[$order['customer_id']]
);
}
Better, one query with a join, or one query for all the customer IDs you need:
$orders = $db->query("SELECT * FROM orders LIMIT 100");
$ids = array_column($orders, 'customer_id');
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$customers = $db->query(
"SELECT * FROM customers WHERE id IN ($placeholders)",
$ids
);
$customersById = array_column($customers, null, 'id');
Two queries instead of 101. On a page with a few hundred rows, that’s the difference between 40ms and 900ms. Profile with your ORM’s query log or a tool like Xdebug’s profiler and you’ll usually find one or two of these hiding in plain sight.
Cache the expensive stuff, but cache it correctly
Redis or Memcached in front of your slowest queries and API calls does more for real-world speed than any micro-optimization. The trap is cache invalidation. Cache things that don’t change often (config, reference data, rendered fragments) and set explicit TTLs so stale data self-heals even when your invalidation logic misses a case.
$key = "product:{$id}:details";
$data = $redis->get($key);
if ($data === false) {
$data = $db->query("SELECT * FROM products WHERE id = ?", [$id]);
$redis->setex($key, 300, json_encode($data));
} else {
$data = json_decode($data, true);
}
A 5-minute TTL on product data means 99% of requests skip the database entirely, and worst case you serve something five minutes stale. For most content that’s a completely acceptable trade.
Smaller things that add up
- Turn off Xdebug in production. It’s a debugging tool and it slows everything down. I’ve seen it left on live servers more than once.
- Use Composer’s optimized autoloader. Run
composer dump-autoload -oduring deploy so class lookups hit a classmap instead of scanning the filesystem. - Enable realpath cache. Bump
realpath_cache_sizeto something like4096kif your app includes a lot of files. - Watch your logging. Synchronous writes to a slow disk on every request will tank throughput. Log to a buffer or a fast local disk, and ship logs asynchronously.
Measure before and after, always
Every change above should be validated. Use ab, wrk, or k6 against a staging environment that mirrors production, and watch both latency and throughput. A change that improves average response time can still hurt p99 under load, and p99 is what your users actually feel when the site gets busy.
If you’d rather not babysit this yourself, this is exactly the kind of work I do through custom web development for clients around Commack and the rest of Long Island. Performance and search visibility tend to go together too, which is why I usually pair server tuning with Long Island SEO work. Faster pages rank better and convert better. It’s the same problem from two angles.
FAQ
Should I enable PHP’s JIT compiler?
For typical database-backed web apps, no. JIT helps CPU-bound code, not request handling that waits on I/O. Benchmark it on your specific workload before turning it on, and don’t expect it to fix slow queries.
How much faster is PHP 8.4 than PHP 7.4?
It varies by app, but a meaningful jump is normal, and you also get security support and modern language features. The exact number depends on your code, so test with your own workload rather than trusting a generic benchmark.
What’s the fastest single fix for a slow PHP site?
Usually fixing N+1 queries and adding OPcache with proper tuning. Those two cover the majority of real-world slowdowns I run into. Micro-optimizations like swapping string functions rarely move the needle by comparison.


