WordPress Behind a Reverse Proxy: Fixing Redirects and HTTPS


In this article
- Why Does WordPress Behind a Reverse Proxy Get Redirects and HTTPS Wrong?
- Step 1: Pass the Right Headers from NGINX to the Origin
- Step 2: Teach wp-config.php to Read HTTP_X_FORWARDED_PROTO
- Step 3: Set the WordPress Site URL Behind the Proxy
- How Do You Fix Mixed Content After Moving to a Proxy?
- Verifying the Setup and Common Mistakes
- FAQ
A WordPress behind reverse proxy setup breaks for one simple reason: WordPress can’t see the protocol and host the visitor actually used. So you forward that information from the proxy layer and let wp-config.php read it. That’s the whole fix, really. Stuck with endless redirects on wp-admin? Mixed content warnings in the browser? Your sitemap full of http:// links and the origin IP? Below I go through the origin NGINX config, wp-config.php, the site URL settings, database cleanup and a last round of checks.
Why Does WordPress Behind a Reverse Proxy Get Redirects and HTTPS Wrong?
WordPress decides whether a request is secure by looking at its own server variables. The visitor connects over HTTPS, the proxy talks to the origin over plain HTTP, and WordPress only ever sees that second, plain leg. So it keeps pushing the browser to the HTTPS version of a page it thinks is insecure. Round and round. The WordPress guide to HTTPS administration says it plainly: a proxy that provides SSL in front of an origin without SSL sends requests into an infinite redirect loop once FORCE_SSL_ADMIN is on. The same page also points out that a proxy pass forwards the request but not the headers WordPress needs for redirects. You have to add those yourself. (Still not sure this architecture is right for your site? Read about when a reverse proxy fits first.)
Step 1: Pass the Right Headers from NGINX to the Origin
Start on the NGINX server you control in front of WordPress. Every proxied location has to forward the original host and scheme, no exceptions. Add these directives to the location block that proxies traffic:
- proxy_set_header Host $host; keeps the public domain instead of the upstream name.
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; passes the visitor’s address down the chain.
- proxy_set_header X-Forwarded-Proto $scheme; tells the origin whether the visitor came in on HTTP or HTTPS.
- proxy_set_header X-Forwarded-Host $host; comes in handy when the origin serves several names or sits behind yet another layer.
What about redirects that leak the upstream address? Covered. The NGINX proxy module documentation explains that proxy_redirect rewrites Location headers and can add host names to relative redirects coming from the proxied server. One thing I’d insist on: trust forwarded headers only when they arrive from known proxy addresses. Whichever service you use to point WordPress at proxy IPs, grab that address list and don’t let anything else set X-Forwarded-* values. Anyone can send a header.
Step 2: Teach wp-config.php to Read HTTP_X_FORWARDED_PROTO
The redirect loop stops once wp-config.php marks the request as secure whenever the proxy reports HTTPS. Drop this snippet above the “That’s all, stop editing” line:
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
$_SERVER['HTTPS'] = 'on';
}
define( 'FORCE_SSL_ADMIN', true );Constants like FORCE_SSL_ADMIN go in wp-config.php. Not in a plugin. The WordPress documentation says defining them in a plugin isn’t enough, and people still try it. To block spoofing, wrap the check in a condition that compares $_SERVER[‘REMOTE_ADDR’] with your proxy addresses. How do you know it worked? wp-admin loads once, without bouncing between URLs.
Step 3: Set the WordPress Site URL Behind the Proxy
Define WP_HOME and WP_SITEURL in wp-config.php with the public HTTPS domain. Never the origin IP, never an internal hostname. WordPress builds every absolute link from these two values, so one wrong entry spreads into the sitemap, canonical tags and RSS feed all at once:
define( 'WP_HOME', 'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );Then go hunting for anything that cached the old address. SEO plugins often keep a generated sitemap that needs regenerating. Saving the permalink settings flushes rewrite rules. And themes? Some keep their own copy of the site URL in options, which is annoying but common. Same discipline applies when moving sites between IP ranges, because search engines pick up whatever URLs your pages expose.
How Do You Fix Mixed Content After Moving to a Proxy?
Mixed content after a proxy switch comes from http:// URLs stored in your database. The proxy isn’t the culprit here. Posts, options and widgets keep whatever address was current when they were saved. Work through these:
- Back up the database first. Then run wp search-replace ‘http://example.com’ ‘https://example.com’ -dry-run to preview the changes before doing it for real.
- Check theme options and page builder data, which love to hold their own copies of image and link URLs.
- Update hardcoded asset URLs in child themes, custom CSS and header scripts.
Use wp-cli, not raw SQL. WordPress stores serialized data with length markers, and a plain text replace will corrupt it (ask anyone who’s had to rebuild a widget area by hand). If certificates or third-party asset hosts are in the mix, review your TLS and CDN footprints too.
Verifying the Setup and Common Mistakes
Check the fix from the outside, the way a visitor and a crawler see the site. Run curl -IL against the public URL and follow the redirect chain. It should end on a single HTTPS page. Open the browser console and look for mixed content warnings. Then load the sitemap and make sure every entry uses HTTPS on the public host.
When it still fails, it’s usually one of a handful of mistakes:
- Forcing HTTPS in NGINX and in a WordPress plugin at the same time. Two sets of redirects, fighting each other.
- Editing the config and forgetting to reload NGINX. Happens more than anyone admits.
- Setting the headers in one location block only, so PHP or admin requests never get them.
- A stale page cache on the origin that keeps serving old http:// markup.
A WordPress behind reverse proxy setup runs reliably once the origin knows the real scheme and host. Forward the headers, read them in wp-config.php, pin the public URL, clean the database. That’s it. For related configuration topics, have a look at the proxy and hosting guides.
FAQ
Why does wp-admin keep redirecting after I put WordPress behind a proxy?
WordPress gets plain HTTP from the proxy and keeps forcing the admin area to HTTPS, so every response triggers yet another redirect. Forward X-Forwarded-Proto from NGINX and set $_SERVER[‘HTTPS’] in wp-config.php when that header says https. The loop stops as soon as WordPress recognises the request as secure.
Do I need an SSL certificate on the origin server?
Not for the fix itself, as long as the link between proxy and origin is trusted. The forwarded header already tells WordPress the visitor used HTTPS. On networks you don’t control, though, I’d still encrypt that hop. It protects logins and cookies on their way to the origin.
Why does my sitemap still show the origin IP or http URLs?
Because WP_HOME, WP_SITEURL or some stored option still holds the old value. Update the constants, run wp search-replace across the database and regenerate the sitemap in your SEO plugin. Then clear any page cache so crawlers get the corrected version.


