Back to tutorials
Tutorial

Reverse Proxy Setup Guide Tutorial (2026): Nginx in Front of cPanel/WHM Apache Without Breaking SSL or Client IPs

Reverse proxy setup guide tutorial for cPanel: add Nginx safely, keep SSL working, preserve real client IPs, and avoid redirect loops.

By Anurag Singh
Updated on Jul 29, 2026
Category: Tutorial
Share article
Reverse Proxy Setup Guide Tutorial (2026): Nginx in Front of cPanel/WHM Apache Without Breaking SSL or Client IPs

Your cPanel server can feel “slow” even when CPU and RAM look fine. A common culprit is Apache doing work better handled elsewhere. It ends up serving static files, holding many keep-alive connections, and soaking up bot traffic. This reverse proxy setup guide tutorial shows how to put Nginx in front of a cPanel/WHM Apache stack on a VPS or dedicated server. You’ll keep AutoSSL working, preserve real client IPs, and avoid redirect loops.

You’ll take a safe baseline snapshot, install Nginx, and connect it to Apache. Then you’ll fix headers and logging. Finally, you’ll verify WordPress, webmail, and WHM still behave normally.

The goal is a practical, 2026-ready setup you can maintain through updates.

What you’ll build (and what you won’t)

  • Front door: Nginx listens on ports 80/443 and proxies dynamic requests to Apache.
  • Origin: Apache keeps running the cPanel stack and serves PHP via whatever you already use (EA-PHP / PHP-FPM).
  • SSL: You keep cPanel AutoSSL/Let’s Encrypt working. Nginx can terminate TLS or pass through; here, Nginx terminates TLS.
  • Real IPs: Apache logs the real visitor IP (not 127.0.0.1), so rate limiting, ModSecurity, and analytics remain accurate.
  • Not included: Kubernetes, service meshes, and other detours that don’t help day-to-day hosting ops.

Prerequisites and a quick safety checklist

Do this on a VPS or dedicated server where you control the network stack. On shared hosting, you usually can’t insert a fronting proxy at the server level.

  • cPanel/WHM server with root access
  • Ubuntu isn’t common for cPanel; most installs are AlmaLinux/Rocky. This tutorial assumes a typical cPanel-supported RHEL-family setup.
  • Working DNS and a test domain you can point at the server
  • SSH access you won’t lose during firewall or port changes

Before you touch anything:

  1. Take a snapshot (VPS) or image backup (dedicated).
  2. Confirm you can log in as root via SSH and via WHM.
  3. Write down your current listening ports: ss -lntp | egrep ':(80|443|2087|2083|2096|2078)\b'

If you want an environment where snapshots and predictable networking are simple, a HostMyCode VPS is a clean place to build and test this. If you’d rather not own the proxy layer day-to-day, managed VPS hosting can take the maintenance burden off your team.

Step 1: Confirm your current Apache behavior (baseline)

Take a baseline now. It makes improvements measurable and regressions obvious.

# Check Apache MPM and current listeners
httpd -V | egrep 'Server MPM|Server version'
ss -lntp | egrep ':(80|443)\b'

# Quick request timing from the server itself
curl -I http://127.0.0.1/ | sed -n '1,10p'

Also confirm nothing else is already bound to 80/443.

It’s not rare to inherit a server where someone installed Nginx “temporarily” and left it running. If Nginx is already listening on 80/443, pause. Inventory the existing configuration before you change anything.

Step 2: Plan the port layout (avoid the classic collision)

Only one service can own ports 80 and 443. In this design, Nginx owns them. Apache moves to high ports on localhost.

  • Nginx: 0.0.0.0:80 and 0.0.0.0:443
  • Apache: 127.0.0.1:8080 and (optionally) 127.0.0.1:8443

Binding Apache to localhost matters. It blocks direct bypass of Nginx. It also keeps firewall rules straightforward.

Step 3: Install Nginx (the boring way that stays maintainable)

On cPanel systems, install Nginx in a way that won’t be brittle during updates. The exact repo story varies by distro.

The target stays the same: a stable Nginx package, systemd-managed, with configs in /etc/nginx.

# AlmaLinux/Rocky style example: install from distro repos
dnf install -y nginx
systemctl enable nginx

Don’t start Nginx yet. If you start it now, it will try to bind 80/443 and collide with Apache.

Step 4: Move Apache off 80/443 (WHM-friendly approach)

On cPanel servers, the “right” approach depends on how your stack is managed. Many admins use a supported plugin/overlay (or a known integration layer). That keeps WHM rebuilds consistent.

If you go manual, assume cPanel updates will overwrite anything you patched in the wrong place.

A practical approach that works in most environments:

  1. Set Apache to listen on 127.0.0.1:8080.
  2. Ensure vhosts bind to that port.
  3. Restart Apache and confirm it no longer owns 80/443.

First, find the active Listen directives. On many cPanel builds they live in generated includes. Don’t guess. Search.

grep -R "^Listen " -n /etc/apache2 /etc/httpd 2>/dev/null | head

Make the change (the file varies; edit the one that actually contains your Listen lines):

# Example (RHEL-family Apache path)
# Edit: /etc/httpd/conf/httpd.conf or an included file

Listen 127.0.0.1:8080
# Remove/disable:
# Listen 80

Restart and confirm:

systemctl restart httpd
ss -lntp | egrep ':(80|443|8080)\b'

If Apache won’t restart, roll back and look for duplicate Listen directives.

Avoid “fixing” it by commenting out random includes. That often breaks later during a cPanel rebuild.

Step 5: Create a minimal Nginx reverse proxy config

Start small. You can tune caching and buffering later.

First, get a clean proxy path with predictable behavior and readable logs.

Create /etc/nginx/conf.d/cpanel-proxy.conf:

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

upstream apache_origin {
  server 127.0.0.1:8080;
  keepalive 32;
}

server {
  listen 80;
  server_name _;

  # If you plan to force HTTPS, do it after verification.
  location / {
    proxy_http_version 1.1;
    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_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_read_timeout 120s;
    proxy_send_timeout 120s;

    proxy_pass http://apache_origin;
  }
}

Test and start Nginx:

nginx -t
systemctl start nginx
ss -lntp | egrep ':(80|443|8080)\b'

HTTP should now flow through Nginx. Verify from your laptop:

curl -I http://YOUR_DOMAIN/

Step 6: Preserve the real client IP in Apache logs

If you stop here, Apache often logs 127.0.0.1 (Nginx) instead of the visitor IP. That makes abuse investigations painful. It also weakens rate limiting.

On Apache, enable and configure mod_remoteip. Paths vary by distro, but the directives look like this:

# Example snippet to add to Apache config (or a conf.d include)
LoadModule remoteip_module modules/mod_remoteip.so

RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 127.0.0.1

Restart Apache and confirm the access log shows your real IP.

Request a page from your own connection, then check:

systemctl restart httpd
# Tail access log location varies
tail -n 5 /var/log/httpd/access_log

If your log format still prints the old value, update the LogFormat to use %a (remote IP after remoteip) instead of %h.

Security work often collides with proxy work.

If you still need to lock down SSH and baseline firewall rules, the HostMyCode guide on UFW firewall setup is a useful companion—especially the port planning sections.

Step 7: Add HTTPS without breaking AutoSSL

You have two sane options:

  • Option A (common): Nginx terminates TLS using certificates on disk; Apache stays HTTP on localhost.
  • Option B: Nginx passes through TLS to Apache (less common with cPanel; harder to add Nginx-only features).

This tutorial uses Option A.

The non-negotiable requirement is certificate hygiene. Keep certs updated. Keep Nginx pointed at the right files as AutoSSL renews.

Where do certs live? cPanel stores domain certs under paths that can vary. The reliable approach is to:

  1. Identify the certificate/key for a specific domain.
  2. Reference it in an Nginx server block.
  3. Hook a reload after renewal (systemd timer or deploy hook).

If you run Let’s Encrypt directly for Nginx/Apache stacks on a VPS (non-cPanel), the HostMyCode tutorial on TLS certificate deployment outlines a repeatable ACME flow. With cPanel AutoSSL, treat Nginx as a consumer of certificates cPanel already issues.

Example HTTPS server block (create another file, e.g. /etc/nginx/conf.d/cpanel-proxy-ssl.conf):

server {
  listen 443 ssl http2;
  server_name example.com www.example.com;

  ssl_certificate     /path/to/fullchain.pem;
  ssl_certificate_key /path/to/privkey.pem;

  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 1d;

  # Modern defaults for 2026 (keep compatible with your audience)
  ssl_protocols TLSv1.2 TLSv1.3;

  location / {
    proxy_http_version 1.1;
    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 https;

    proxy_pass http://apache_origin;
  }
}

Test and reload:

nginx -t
systemctl reload nginx

Once HTTPS is confirmed, add the HTTP → HTTPS redirect:

# Add inside the :80 server block
return 301 https://$host$request_uri;

Step 8: Fix redirect loops (the #1 failure mode)

Redirect loops usually come from one of these:

  • WordPress thinks the site is HTTP because Apache only sees HTTP from Nginx.
  • Your app forces HTTPS, but Nginx already redirected and the headers don’t match.
  • Apache rewrites based on %{HTTPS} instead of X-Forwarded-Proto.

Quick diagnostics:

curl -I http://example.com/ | egrep 'HTTP/|Location:'
curl -I https://example.com/ | egrep 'HTTP/|Location:'

Fixes that usually work:

  • Make sure Nginx sends X-Forwarded-Proto on both 80 and 443 paths.
  • In WordPress, verify WP_HOME and WP_SITEURL match https if you hard-coded them.
  • If you run a caching/WAF layer, confirm it isn’t overwriting scheme headers.

If you’re proxying WordPress specifically, HostMyCode’s Nginx reverse proxy for WordPress goes deeper on common gotchas (admin-ajax, login cookies, and mixed content checks).

Step 9: Keep cPanel services reachable (WHM, cPanel, webmail)

cPanel/WHM uses its own ports (2087/2083/2096/2078, etc.). This reverse proxy is only for web traffic on 80/443.

Don’t proxy WHM through public Nginx unless you have a clear reason and a safe access plan.

Double-check your firewall rules for the ports you actually use for administration.

A safer pattern is to avoid exposing WHM publicly and use a bastion/jump host or a VPN. If you want a cleaner admin access model, follow the HostMyCode tutorial on SSH jump host setup.

Step 10: Tune Nginx for static files (easy wins without risky caching)

Even without full-page caching, Nginx can take pressure off Apache by serving static assets directly. Keep the rule simple.

Only serve truly static files, and fall back to Apache for everything else.

Add to your HTTPS server block:

location ~* \.(css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2?)$ {
  expires 30d;
  add_header Cache-Control "public, max-age=2592000";

  # Try to serve from disk; if not present, proxy to Apache
  try_files $uri @apache;
}

location @apache {
  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 https;
  proxy_pass http://apache_origin;
}

This only works well if Nginx can read the same docroot paths Apache uses. On cPanel servers with per-account home directories, that can get messy fast.

If your paths aren’t consistent, skip this and keep Nginx as a pure proxy.

Step 11: Logging and troubleshooting (what to check first)

When something breaks, don’t start guessing. Run a short set of checks. Then change one thing at a time.

  • Nginx config valid: nginx -t
  • Nginx error log: tail -n 50 /var/log/nginx/error.log
  • Apache status: systemctl status httpd --no-pager
  • Apache error log: tail -n 50 /var/log/httpd/error_log
  • Port ownership: ss -lntp | egrep ':(80|443|8080)\b'

If you hit TLS issues (wrong certificate, broken chain, renewals failing), the workflow in HostMyCode’s TLS certificate renewal troubleshooting tutorial is quick and practical. Even if you’re not using Let’s Encrypt directly, the same checks (SNI, chain validation, OCSP behavior) apply.

Step 12: A practical post-change validation checklist

Run this after you enable redirects and before you call the job done.

  1. Homepage loads over HTTP and HTTPS.
  2. Login flows work (WordPress admin, WooCommerce checkout, any session-based app).
  3. Real IPs show up in Apache logs (not 127.0.0.1).
  4. No mixed content warnings in the browser console.
  5. AutoSSL/renewals still run on schedule; Nginx reload happens after renewal (if required by your setup).
  6. Headers are sane: curl -I https://example.com/ shows expected server, cache, and security headers.
  7. Resource usage improved: compare top, Apache workers, and load average during peak.

Common pitfalls (and how to avoid them)

  • Binding conflicts: Apache and Nginx both try to own 80/443. Validate with ss -lntp after restarts.
  • Proxying everything including admin ports: Don’t. Keep WHM/cPanel ports separate unless you’ve designed secure access around it.
  • Certificate drift: cPanel rotates/renews; Nginx keeps serving an old file. Pick one source of truth and automate reloads.
  • Broken client IP: Missing mod_remoteip config. Fix this early, before tuning security rules.

Summary: what you gain from an Nginx front layer

With Nginx at the edge, Apache spends less time managing connections. It can spend more time on dynamic work.

On busy shared-style servers, that typically means fewer latency spikes during bursts. You also get better tolerance of noisy crawlers.

It’s not magic. It’s simply less overhead per request.

If you want a stable place to run this pattern—snapshots, consistent networking, and headroom for sudden load—start on a HostMyCode VPS. If you’d rather have the proxy layer, updates, and monitoring handled for you, managed VPS hosting is the direct fit for production cPanel stacks.

If you’re adding Nginx to steady a busy cPanel server, run it on infrastructure that supports snapshots, predictable performance, and clean IP ownership. HostMyCode offers both VPS hosting and managed VPS hosting for teams that want the tuning, patching, and monitoring handled end-to-end.

FAQ

Will this break cPanel AutoSSL?

Not if you keep the certificate workflow clear. The usual failure is Nginx continuing to serve a stale certificate after AutoSSL renews.

If AutoSSL rotates files, automate an nginx -t && systemctl reload nginx step after renewal.

Do I need to proxy WHM/cPanel through Nginx?

No. Keep WHM (2087) and cPanel (2083) separate. If you want tighter admin exposure, use an SSH jump host and restrict firewall access instead of adding another proxy hop.

How do I confirm Apache sees the real visitor IP?

Hit your site from your own IP, then check Apache access logs. If you see 127.0.0.1, enable mod_remoteip and make sure Nginx sends X-Forwarded-For.

Update the Apache log format to use %a.

What’s the safest first performance tweak after the proxy works?

Start with static asset caching headers and gzip/brotli only after you’ve validated application behavior. Avoid full-page caching until you’ve mapped logged-in vs anonymous traffic and confirmed correct cache keys.