Tutorial · migration-hosting · Published 2026-08-16 · 4 min read
Moving a website from Apache to nginx
Apache to nginx migration: port .htaccess rules, wire PHP-FPM, map modules and extensions, and verify URLs, redirects and rewrites after the switch.
The migration is config, not content
Moving a site from Apache to nginx (a common host change where the panel or a new server runs nginx, or a micro-optimisation to gain the file-serving edge) does not touch your HTML, files, or database. It transplants the server configuration. The rules that Apache lived in .htaccess and httpd.conf must become nginx location and rewrite blocks in nginx.conf, and PHP handling changes from Apache's module to a PHP-FPM pool.
Where the two differ for a migration:
| Concern | Apache | nginx |
|---|---|---|
| Config location | .htaccess per directory | One server block, per-site .conf file |
| Rewrites | RewriteRule in .htaccess | rewrite, location blocks, try_files |
| PHP | mod_php or a FastCGI handler | PHP-FPM via fastcgi_pass |
| Directory access | Per-directory Directory sections | location / and try_files |
The host migration checklist is the transport side (files, DB, DNS); this article is the *config side* of the same move.
Step 1: inventory .htaccess
Every line in .htaccess (and any Directory/VirtualHost block in the Apache config) needs a translation. Read it first and sort the rules by pattern:
RewriteEngine On
RewriteRule ^products/([0-9]+)/?$ product.php?id=$1 [L]
in nginx:
location /products {
rewrite ^/products/([0-9]+)/?$ /product.php?id=$1 last;
}
The mapping is mechanical for simple rules but the edge cases bite: a rule that uses [R=301] (a permanent redirect) must keep its full URL and permanent, while a rule that only changes the internal path uses last and no R. Mixing the two is how an "Apache to nginx" migration silently turns every old 301 into a broken redirect.
Step 2: map the rewrite flags
| Apache | nginx | Meaning |
|---|---|---|
[R=301] | redirect permanent | Client-visible 301 |
[L] | last | Stop processing this rule set |
[NC] | (?i) prefix in the pattern | Case-insensitive |
.htaccess honoured per-dir | location blocks | Scope the rules to a path |
Ignore rules you do not need: a .htaccess often contains old Options and AddType lines from a panel that nginx does not use at all.
Step 3: wire PHP and FastCGI
nginx does not run PHP through a module; it describes a location that hands .php requests to a PHP-FPM listener:
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Notes:
- The socket path must match your PHP-FPM pool (
pool.d/www.conf), not a guess. - A wrong
SCRIPT_FILENAMEis the most common "nginx returns empty 200" on a WordPress/php site. .phpfiles not underlocation ~ \.php$get served as text unless the block covers them; that is a file-disclosure leak for a site with a stray.phpfile anywhere in the tree.
Step 4: The WordPress-specific part
WordPress ships a .htaccess for pretty permalinks. In nginx, that rest is try_files:
location / {
try_files $uri $uri/ /index.php?$args;
}
Every CMS has this one line, and it is the difference between the homepage working and every page 404. The redirects article has the general 301/redirect semantics that apply if you move URL structure at the same time.
Step 5: Verify like a build gate
Before the DNS cutover (or in a temporary domain), test on the new server:
- The homepage and a deep path (the rewrite above should serve both).
- A 301 rule:
curl -I https://new.example.com/old-pagereturns301and the rightLocation. - PHP:
curl -I https://new.example.com/index.phpreturns HTTP 200, not text or a 404. - The exact paths from
.htaccessthat redirect: run the same list against both servers and diff the status codes.
Use the hosts-file trick from the host migration checklist so the new stack answers your domain before the switch.
Prevention
- Keep a plain copy of the old
.htaccessand the translated.confin the site repo, so a re-move or a compare is one file away. - Test the translated rules on staging first, because a mangled rewrite on live is the difference between a slow migration and a broken one.
- If the host panel offers an Apache/nginx switch, the same checklist applies: the
.htaccessstill needs its translation, even when the button is one click.
The neighbouring articles cover the transport (moving hosts and DNS) and the redirect semantics that make the URL rules correct once the server software changes.