My app structure uses GKE and CloudFlare. It looks like this:

CloudFlare -> GKE -> Ingress -> My app running nginx

I'm using the flexible SSL in CloudFlare, so only the connection between the user and CloudFlare uses HTTPS, all the remaining uses HTTP. I know CloudFlare sets the X-Forwarded-Proto to https in this situation, but when I see the headers my nginx app is receiving, it gets X-Forwarded-Proto: http.

I'm pretty sure this happens somewhere between GKE's Load Balancer and Ingress, as I can see that the CF-Visitor: {"scheme": "https"} header configured by CloudFlare is set to HTTPS. My understanding is that this means that CloudFlare did set X-Forwarded-Proto to https, but it got overwritten along the way.

Unfortunately, I couldn't get the header logs from the GKE Load Balancer (it seems they don't log the X-Forwarded-* headers at all), so I can't confirm 100% that CloudFlare is actually setting the headers, but I'd be pretty surprised if it isn't.

If that's true, Google Cloud is overwriting the X-Forwarded-Proto header with http. How can I avoid it doing so?

Edit: I have configured an nginx ingress instead of gce following https://cloud.google.com/community/tutorials/nginx-ingress-gke, and the X-Forwarded-Proto is set to https as expected. This is another signal that it's the gce ingress controller that is overwriting the X-Forwarded-Proto header.

  • 1
    It's quite normal to overwrite X-Forwaded-* headers from random untrusted clients. Yes, there should be a method to set trusted clients/proxies, good question. – kubanczyk May 14 '18 at 19:25
  • Check to see if you have more than one header value. Well-written proxies and load balancers should insert their value and not overwrite giving you a chain of values. I have not verified this will GCP however. – John Hanley Oct 7 '19 at 0:16

As described in this article Cloudflare appends an X-Forwarded-Proto header which can either be HTTP or HTTPS depending on the protocol the user used to visit the site. If you believe the value of X-Forwarded-Proto should be maintained but was changed by GCLB, I'd recommend opening a feature request for this on Google issue tracker.


You can create a middleware:

# frozen_string_literal: true

require 'json'

class CloudflareProxy
  def initialize(app)
    @app = app

  def call(env)
    return @app.call(env) unless env['HTTP_CF_VISITOR']

    env['HTTP_X_FORWARDED_PROTO'] = JSON.parse(env['HTTP_CF_VISITOR'])['scheme']

Use in config/application.rb:

config.middleware.use CloudflareProxy

A reference for CF-Visitor header:



A JSON object containing only one key called scheme. The value is identical to that of X-Forwarded-Proto (either HTTP or HTTPS). CF-Visitor is only relevant if using Flexible SSL.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.