Running Nginx in front of Apache combines Nginx's efficient handling of connections and static files with Apache's flexibility and .htaccess support. It is the model Plesk uses by default and a common way to speed up an existing Apache server without rewriting every site's rules. This guide builds the hybrid setup on a self-managed VPS.
Before you start
- A VPS with Apache already serving your sites, and root access.
- A maintenance window: ports move from Apache to Nginx.
- Consider the alternatives first: LiteSpeed Enterprise gives similar benefits in a single server with full .htaccess support. See Apache vs Nginx vs LiteSpeed.
How the hybrid setup works
- Nginx listens publicly on 80 and 443 and terminates TLS.
- Requests for static files (images, CSS, JS) are served directly by Nginx from disk.
- Everything else is proxied to Apache on
127.0.0.1:8080, which runs PHP (via PHP-FPM) and applies .htaccess rules. - Nginx buffers Apache's response and feeds it to slow clients, freeing Apache workers quickly.
Move Apache to port 8080
- Edit
/etc/apache2/ports.conf(or/etc/httpd/conf/httpd.conf): changeListen 80toListen 127.0.0.1:8080, and removeListen 443. - In every vhost change
<VirtualHost *:80>to<VirtualHost 127.0.0.1:8080>. Disable the HTTPS vhosts; TLS moves to Nginx. - Don't restart Apache yet.
Restore real client IPs in Apache
Enable mod_remoteip so Apache logs and apps see the visitor's IP, not 127.0.0.1:
sudo a2enmod remoteipCreate /etc/apache2/conf-available/remoteip.conf:
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1
SetEnvIf X-Forwarded-Proto https HTTPS=onEnable it with a2enconf remoteip. Replace %h with %a in your LogFormat so logs show the real IP. The HTTPS=on line stops apps from looping redirects to HTTPS.
Configure Nginx
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
root /var/www/example.com/public;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location ~* \.(?:css|js|jpe?g|png|gif|webp|avif|svg|ico|woff2?)$ {
expires 30d;
access_log off;
try_files $uri @apache;
}
location / {
try_files /nonexistent @apache;
}
location @apache {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
}
location ~ /\.ht { deny all; }
}- Static files are served by Nginx if they exist; otherwise they fall through to Apache (so rewritten asset URLs still work).
- Nginx's
rootmust match Apache's DocumentRoot. - Deny
.htaccessand.htpasswd: Nginx would otherwise serve them as plain files.
Switch over
sudo apachectl configtest && sudo nginx -tsudo systemctl restart apache2 && sudo systemctl restart nginx- Issue or move certificates to Nginx:
sudo certbot --nginx -d example.com -d www.example.com. - Verify:
curl -sI https://example.com/showsServer: nginx, and PHP pages still work.
Optional: cache Apache's responses
Once Nginx proxies Apache, you can add proxy_cache for anonymous visitors. See Nginx proxy_cache and microcaching.
Is the hybrid setup worth it?
- Yes when you have many sites relying on .htaccess and Apache workers are tied up by slow clients or static files.
- Maybe not if Apache already uses the event MPM with PHP-FPM and HTTP/2; the gain is then smaller, and you maintain two servers.
- Gotcha: rules in .htaccess don't apply to files Nginx serves directly (e.g. hotlink protection or access restrictions on images). Replicate them in Nginx.
Troubleshooting
- Redirect loop: Apache doesn't know the request was HTTPS. Check
SetEnvIf X-Forwarded-Protoand the app's proxy settings. - 502 Bad Gateway: Apache isn't listening on 127.0.0.1:8080.
- Logs show 127.0.0.1: mod_remoteip not enabled or LogFormat still uses
%h.
FAQ: Nginx in front of Apache
Does WordPress work in this setup?
Yes. Apache still processes its .htaccess rewrite rules.
Which server handles HTTPS?
Nginx. Apache only receives plain HTTP from localhost.
Is this how cPanel works?
cPanel can run Nginx as a caching proxy in front of Apache, but MdmHost uses LiteSpeed instead, which needs no proxy layer.
