Setting up SSL for Nginx behind a Load Balancer


#1

I’ve sort of got this set-up working but I would like some advice on how to do it better.

I’m running a high availability set-up on AWS and have an Application Elastic Load Balancer in front of two servers. The load balancer handles SSL termination and passes the request to port 80 of the servers. The servers are configured to look for the X-FORWARDED-PROTO. If X-FORWARDED-PROTO is not equal to https then we redirect to the HTTPS version of the URL. Everything is working swell there.

My issue is Nginx is listening on port 80 and I’m using Redis cache for full-page caching. The URLs being used for cache keys are httpGETexample.com/some/random/path/ instead of httpsGETexample.com/some/random/path/ This means cached items can’t be purged via the Nginx Helper plugin since the URL in the admin is https What’s the best way to fix this?

Should my servers be listening to the load balancer on port 443? Do they need their own certificate to communicate between the load balancer and the server? This seems to defeat the purpose of SSL termination at the load balancer level.

Is there some way to specify that if X-FORWARDED-PROTO equals https then to tell nginx the protocal is HTTPS? How would I do that?

Here’s a version of my nginx conf for the site:

server {
    listen 80;
    listen 443;

    server_name example.com example.org example.net ;

    set $redirect_to_https 0;
    if ( $http_x_forwarded_proto != 'https' ) {
      set $redirect_to_https 1;
    }
    if ( $request_uri = '/health-check.php' ) {
      set $redirect_to_https 0;
    }
    if ( $redirect_to_https = 1 ) {
      return 301 https://$host$request_uri;
    }

    # Uncomment the following line for domain mapping
    server_name_in_redirect off;

    access_log /var/log/nginx/example.com.access.log rt_cache_redis;
    error_log /var/log/nginx/example.com.error.log;


    root /var/www/example.com/htdocs;
    index index.php index.html index.htm;

    include  common/redis-php7.conf;

    include common/wpcommon-php7-modified.conf;
    include common/locations-php7.conf;
    include /var/www/example.com/conf/nginx/*.conf;
}

#2

Somewhat related:

Since I’m receiving the request on port 80 I need to tell WordPress that the request is really an HTTPS request. I do the following in wp-config.php which works but this should probably be done at the Nginx level

/*
Set $_SERVER variables if the request is being passed from an HTTPS request from the load balancer. Otherwise is_ssl() doesn't work and we get endless redirects
*/
if ( 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
  $_SERVER['HTTPS'] = 'on';
  $_SERVER['SERVER_PORT'] = '443';
}

If I don’t do this then is_ssl() returns false and wp-login.php goes into an infinite redirect loop.


#3

The easiest thing for me to do was to filter get_site_url whenever it was being called from the Nginx Helper plugin like so:

add_filter( 'site_url', function( $url = '' ) {
    $backtrace = wp_debug_backtrace_summary();
    if ( stripos( $backtrace, 'rtCamp\WP\Nginx\Helper' ) ) {
        $url = str_replace( 'https://', 'http://', $url );
    }
    return $url;
});

Now when updates are made the cache is purged appropriately.


#4