Move a PHP app from Apache to Nginx with PHP-FPM
Moving a PHP application from Apache to Nginx with PHP-FPM changes more than the process listening on port 80. Apache currently combines HTTP handling, URL rewriting, static delivery and PHP execution; the replacement splits those duties between Nginx and a FastCGI worker pool. A safe migration therefore treats request handling as a contract, not as a server preference. Learn more about Msi Afterburnerでフレームレートキャップを設定して消費電力を抑える方法と遅延への影響.
For Australian teams, the contract must survive both geography and usage. Sydney and Melbourne hosting regions can keep latency sensible, while customers using mobile services or NBN connections, including Fibre-to-the-Node, can expose the application to interruptions and timeouts. The application should also account for its audit trail and privacy controls under the Privacy Act 1988 and applicable state laws.
Audit Apache behaviour before changing servers
Start by exporting every virtual host, alias, directory index and .htaccess file. Record active Apache modules, rewrite rules, redirects, authentication requirements, MIME types, compression, cache headers, custom error pages and security rules. Shared Australian hosting based on cPanel often makes Apache rules part of the site contract, even when the application code never mentions them.
Turn those findings into a URL parity matrix: public path, method, expected status, headers, body and destination. Include uploads, webhooks, language routes, trailing slashes, encoded characters and query strings. Test the behaviour that clients actually consume rather than comparing only homepages.
Choose the target from workload and operating ownership, not from a desire to use Nginx everywhere. If the target is still undecided, the comparison at https://karlkatzke.com/cloud-vs-in-house-infrastructure helps separate application architecture from hosting ownership. Record expected concurrency, memory per worker, traffic peaks and acceptable latency before moving a socket.
Design the Nginx and PHP-FPM boundary
Nginx should terminate TLS, serve eligible static assets, route requests and enforce request-level limits. PHP-FPM should own PHP execution through a Unix socket or a protected TCP listener. A Unix socket avoids unnecessary network handling on one host; TCP becomes reasonable for separate application and worker tiers, provided authentication, encryption and network policy are explicit.
A generic front-controller configuration might begin like this, with paths adapted to the application:
server {
root /srv/example/current/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
Give each application its own FPM pool and operating-system user. Set pm.max_children from measured memory per process rather than copying a common default. Define php_admin_value entries for upload limits, execution limits and error settings where needed, and expose pool status only through a protected route. If the web tier scales horizontally, use shared sessions or deliberate stickiness so users are not logged out during failover.
Translate rewrite and hosting rules
Nginx does not read .htaccess, so those rules must be translated into server or location configuration. Apache RewriteRule patterns, QSA, RewriteBase and internal redirects do not map one-for-one to Nginx rewrite and try_files. Preserve query strings, slash behaviour and redirect status codes, then test rules in order because a front controller can easily create a rewrite loop.
Audit directory-specific authorisation, mod_auth_basic, mod_security, MIME handling, compression and expiry headers separately. Conditional authorisation may require auth_request; server-side includes, WebDAV methods or Apache-only modules may need an application change or an upstream proxy. Raise client_max_body_size deliberately for uploads, and reproduce custom 404 and 50x handling instead of allowing infrastructure pages to leak through.
PHP settings also move. The pool's php.ini, .user.ini, environment variables, open_basedir, session path and working directory must match the old runtime. Confirm that release directories, writable logs, sessions, temporary uploads and the vendor tree have the correct ownership. Run scheduled jobs through PHP CLI with the same extensions and environment as the web process, and warm or reset OPcache deliberately during deployment.
Build a repeatable migration checklist
Use repeatable gates rather than relying on a final click-through test:
- Export all hosts, aliases and
.htaccessfiles. - Map each public route to its expected response.
- Record PHP, Composer, database and system versions.
- Reproduce TLS, DNS, firewall and mail settings.
After the foundation is staged, verify the behaviour that carries the most business risk:
- Compare status codes, headers, redirects and response bodies.
- Exercise login, checkout, uploads, webhooks and email flows.
- Load-test bursts while watching memory, workers and queueing.
- Verify cron timing across Australian Eastern Standard Time and daylight-saving changes.
- Rehearse rollback and confirm application logs remain searchable.
Store each result with the commit, configuration revision and test data. A parity matrix should mark failures as release blockers, accepted differences or tracked follow-up work. This prevents a known Apache quirk from disappearing into deployment notes.
Test from Australian networks
Test from Australian conditions rather than only from the server console. Run synthetic probes through Sydney and Melbourne, then repeat over mobile and fixed broadband paths, including a lossy or interrupted connection. Confirm that browser retries, API clients and webhook senders receive predictable responses when latency rises or a worker is busy.
Watch Nginx status codes, upstream timing, FPM queue depth, worker saturation, CPU, memory, database latency and PHP error rate. Compare those signals before and after the change. Resource ceilings need measurement rather than folklore. The Afterburner frame rate cap guide illustrates the same discipline for a graphics workstation used in load generation: constrain one variable, then check power and latency instead of assuming a lower limit is always better.
Before cutover, verify backups, log shipping, uptime monitoring and certificate renewal paths. Define a rollback trigger using observed symptoms, such as sustained 502 responses, checkout failures or abnormal worker growth. A traffic-weighted canary is more informative than a single synthetic request.
Roll out with a reversible release plan
Lower the relevant DNS TTL in advance, then shift traffic gradually through a load balancer or origin target while Apache remains warm. Keep the old application release available until error rates, latency and key transactions stay within agreed limits. DNS caches in Australian networks can extend the transition, so rely on measured traffic rather than a calendar estimate.
Check compliance as part of release acceptance. The Australian Privacy Principles and Notifiable Data Breaches scheme apply to eligible organisations; health information may also engage Victorian or New South Wales privacy legislation. Ensure logs, debug output and support screenshots do not expose personal information, and rehearse incident escalation with the people who must make notifications.
After the switch, retain both stacks for an agreed observation window and compare application logs with Nginx and FPM logs. Schedule follow-up work for genuine improvements, but do not alter several layers during the canary, since that makes cause and effect ambiguous. Record the final configuration and recovery procedure in the runbook.
Create the URL parity matrix today, assign owners to every failed behaviour check, and schedule the first production canary for a low-traffic Australian business window. That gives the team a concrete migration path, observable success criteria and a safe route back to Apache if the evidence calls for it.
Karl Katzke