Linux VPS

Run Nginx in front of Apache: the hybrid reverse proxy setup

Put Nginx on ports 80/443 to handle TLS, static files and slow clients while Apache on 8080 keeps .htaccess support for PHP apps. Config, real IP forwarding and when this is worth it.

AdvancedLinux VPS3 min read

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

  1. Nginx listens publicly on 80 and 443 and terminates TLS.
  2. Requests for static files (images, CSS, JS) are served directly by Nginx from disk.
  3. Everything else is proxied to Apache on 127.0.0.1:8080, which runs PHP (via PHP-FPM) and applies .htaccess rules.
  4. Nginx buffers Apache's response and feeds it to slow clients, freeing Apache workers quickly.

Move Apache to port 8080

  1. Edit /etc/apache2/ports.conf (or /etc/httpd/conf/httpd.conf): change Listen 80 to Listen 127.0.0.1:8080, and remove Listen 443.
  2. In every vhost change <VirtualHost *:80> to <VirtualHost 127.0.0.1:8080>. Disable the HTTPS vhosts; TLS moves to Nginx.
  3. 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 remoteip

Create /etc/apache2/conf-available/remoteip.conf:

RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1
SetEnvIf X-Forwarded-Proto https HTTPS=on

Enable 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 root must match Apache's DocumentRoot.
  • Deny .htaccess and .htpasswd: Nginx would otherwise serve them as plain files.

Switch over

  1. sudo apachectl configtest && sudo nginx -t
  2. sudo systemctl restart apache2 && sudo systemctl restart nginx
  3. Issue or move certificates to Nginx: sudo certbot --nginx -d example.com -d www.example.com.
  4. Verify: curl -sI https://example.com/ shows Server: 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-Proto and 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.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.