Prevent SSL redirect loop using WordPress and HAProxy

This is a first post in a series on how to use HAProxy in front of WordPress. I’m using HAProxy to offload SSL connections to a WordPress site. The site itself runs on an internal IP address on port 80 while HAProxy listens on incoming connections on *:80 and *:443. Connections to *:443 will be presented the correct certificate using HAProxy’s SNI-based certificate matching algorithm. I’ll write more about that SNI-based configuration in a future post. In this post I’m going to focus on the SSL redirect loop which is happening if you use

define('FORCE_SSL_ADMIN', true);

and/or

define('FORCE_SSL_LOGIN', true);

in wp-config.php. Since HAProxy offloads the SSL connection, the web server running the WordPress site has no way to know the connection was SSL-based initially. Edit wp-config.php and add the following lines:

define('FORCE_SSL_ADMIN', true);
define('FORCE_SSL_LOGIN', true);
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
  $_SERVER['HTTPS']='on';

We’re telling WordPress that if an X-Forwarded-Proto header is present with the value https, the connection was initially based on SSL. Obviously, the X-Forwarded-Proto header has to be injected into the HTTP header of the request by HAProxy in the first place:

frontend ft_web_ssl
  mode http
  bind 0.0.0.0:443 ssl crt /etc/haproxy/certs.d
  reqadd X-Forwarded-Proto:\ https
  ...
  ...

Make sure to use option http-server-close as well or the reqadd setting might not work as expected.

8 thoughts on “Prevent SSL redirect loop using WordPress and HAProxy

  1. It should be noted that you cannot ‘add’ this to your wp-admin.php. It has to be before your wp-settings.php. So the last few lines should look like:

    /** Absolute path to the WordPress directory. */
    if ( !defined(‘ABSPATH’) )
    define(‘ABSPATH’, dirname(__FILE__) . ‘/’);

    define(‘FORCE_SSL_ADMIN’, true);
    define(‘FORCE_SSL_LOGIN’, true);
    if ($_SERVER[‘HTTP_X_FORWARDED_PROTO’] == ‘https’)
    $_SERVER[‘HTTPS’]=’on’;

    /** Sets up WordPress vars and included files. */
    require_once(ABSPATH . ‘wp-settings.php’);

    .. if you bluntly add it (below the wp-settings.php), you will not be able to login to your wp-admin anymore (“you do not have sufficient permissions to access this page”).

  2. Thanks a lot man!!!
    I spent hours on this, reading other blogs without success.
    What saved me is the part :

    if ($_SERVER[‘HTTP_X_FORWARDED_PROTO’] == ‘https’)
    $_SERVER[‘HTTPS’]=’on’;

    Finally, I did not even have to change the home URL in WordPress parameters.

  3. This has been very helpful. Thanks
    We have a jetNEXUS load balancer hosted in Azure and have used flightpath to add the http headers. We are now using the load balancer for SSL offload with WordPress and all is well.

  4. Thanks!
    Happened to me when i switched to a slighlty different setup (from kvm to lxc)
    I was using a reverse proxy (nginx) before too – so i don’t know what exactly caused this problem in my case.

    Kind Regards,
    faljse

  5. Well, Chad, it is just that that then , you will always force https on.. which is fine if that is what you want, but then you don’t need to force sll login and admin on , do you :)

    Your logic is: if rev-proxied, do ssl, which is not the same as
    if https && proxied , pass ssl-header -> ssl.

  6. Thanks for the help. This was quite frustrating.

    I ended up using a slightly different approach though. Since I already had “option forwardfor” on my haproxy config and didn’t want to add an additional request, I simply check for the existence of “HTTP_X_FORWARDED_FOR”. Here are the lines in my wp-config.php:

    define(‘FORCE_SSL_ADMIN’, true);
    define(‘FORCE_SSL_LOGIN’, true);
    if ($_SERVER[‘HTTP_X_FORWARDED_FOR’])
    $_SERVER[‘HTTPS’]=’on’;

Comments are closed.