Override $scheme in nginx When Behind Load Balancer

kingkool68 asked:

I’ve got an nginx server sitting behind a load balancer. The load balancer handles SSL termination with all requests hitting nginx on port 80. I’m also using the SRCache module for full page caching using Redis. The caching module uses the URL as a cache key like $schemeGET$host$request_uri. I was thinking I could just override nginx’s $scheme variable somehow so the cache key scheme would be https instead of http I’m not sure how to do that or if it is even possible.

My app does cache purging after various events and it generates the cache key using https but nginx is caching using http in the cache key. This means the cache is not purged properly due to the missmatched cache key names.

Here is my site configuration if that helps:

server {
listen 80;

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;

UPDATE Here is the caching configuration

set $skip_cache 0;
# POST requests and URL with a query string should always go to php
if ($request_method = POST) {
  set $skip_cache 0;
if ($query_string != "") {
  set $skip_cache 1;
# Don't cache URL containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|wp-.*.php|index.php|/feed/|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
  set $skip_cache 1;
# Don't use the cache for logged in users or recent commenter
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
  set $skip_cache 1;
# Use cached or actual file if they exists, Otherwise pass request to WordPress
location / {
  try_files $uri $uri/ /index.php?$args;

location /redis-fetch {
    internal  ;
    set  $redis_key $args;
    redis_pass  redis;
location /redis-store {
    internal  ;
    set_unescape_uri $key $arg_key ;
    redis2_query  set $key $echo_request_body;
    redis2_query expire $key 14400;
    redis2_pass  redis;

location ~ \.php$ {
  set $key "nginx-cache:$scheme$request_method$host$request_uri";
  try_files $uri =404;

  srcache_fetch_skip $skip_cache;
  srcache_store_skip $skip_cache;

  srcache_response_cache_control off;

  set_escape_uri $escaped_key $key;

  srcache_fetch GET /redis-fetch $key;
  srcache_store PUT /redis-store key=$escaped_key;

  more_set_headers 'X-SRCache-Fetch-Status $srcache_fetch_status';
  more_set_headers 'X-SRCache-Store-Status $srcache_store_status';

  include fastcgi_params;
  fastcgi_pass php7;

My answer:

OK, so here we see your cache key that is being used for redis lookups:

location ~ \.php$ {
  set $key "nginx-cache:$scheme$request_method$host$request_uri";

The problem is that $scheme reflects the connection that was made to nginx (from your load balancer), but your caching module is using the scheme from $http_x_forwarded_proto, which reflects the actual scheme in use.

It should be sufficient to just make that change.

View the full question and any other answers on Server Fault.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.