How to get Wordpress working with HTTPS behind a reverse proxy

"I am Anders, I am a PHP user." Support group: "Hi Anders!"

In my last post I described how to get HTTP/2 working with Caddy. Now I'm going to tell you how to get a Wordpress blog working behind it. Caddy is called a reverse proxy since it handles requests coming in to a network (a "regular" proxy handles traffic going out of a network to filter/cache client requests).

What Caddy delivers

A simple Caddy configuration (in the Caddyfile) for a Wordpress blog could be:

https://www.osirisguitar.com {
  tls anders@bornholm.se
  proxy / osiris-guitar-osirisguitar:80 {
    transparent
  }
  log stdout
}

This means that requests to https://www.osirisguitar.com will be sent to port 80 on the container linked to Caddy by the name osiris-guitar-osirisguitar.

The directive transparent is important here. What it does it make Caddy send the following headers to the underlying service (the Wordpress blog):

header_upstream Host {host}
header_upstream X-Real-IP {remote}
header_upstream X-Forwarded-For {remote}
header_upstream X-Forwarded-Proto {scheme}

These are standardized headers for reverse proxies to send to underlying services so that they know what host name, IP etc. the original request was actually sent to.

How HTTPS works in Wordpress

In Wordpress you can set the URL of your blog in the admin UI (or in wp-config.php).


If the url fields are greyed out, they are set in wp-config.php and can't be changed in the UI

However, setting this to https://yourdomain.com will not work. The start page of your blog will be served on this new url proxied through Caddy, but all resources like scripts, stylesheets and images will still be served through http://yourdomain.com.

The culprit is the function is_ssl() in Wordpress core. This function will use the protocol and url of the request to create the correct urls to resources. The problem is that it uses the request to the container instead of the headers provided by Caddy.

Fixing is_ssl()

When running on a standard platform like Wordpress, you really don't want to change stuff in the core (it can break upgrades etc). Especially not when you're running in Docker. Fortunately, is_ssl() can be fixed by setting some server variables in wp-config.php based on the proxy headers:

if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
  $_SERVER['HTTPS']='on';
}

if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
  $_SERVER['HTTP_HOST'] =  $_SERVER['HTTP_X_FORWARDED_HOST'];
}

This means $_SERVER['HTTPS'] will be 'on' and $_SERVER['HTTP_HOST'] will be 'www.osirisguitar.com' (in my case) and in turn is_ssl() will work and so will your blog.

comments powered by Disqus