see #1766 for some people having the same issue
I have the following in my nginx config:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
This causes an error because the default setting for secure_scheme_headers
is {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
. When it compares the ssl
to the https
the error is thrown in https://github.com/benoitc/gunicorn/blob/19.9.0/gunicorn/http/message.py#L118
You're correct about the cause. The value for X-Forwarded-Protocol
should be ssl
but $scheme
will be http
or https
. You can either change your nginx configuration to set X-Forwarded-Protocol
to ssl
conditionally, based on the value of $scheme
; change secure_scheme_headers
; or remove proxy_set_header X-Forwarded-Protocol
altogether.
I'm not familiar with any software that sets X-Forwarded-Protocol
(only X-Forwarded-Proto
), so I can't say whether the default secure_scheme_headers
are correct.
I don't see any bug in Gunicorn. Thankfully, this seems like it could be solved with configuration changes.
From the reading I did, it seems that X-Forwarded-Protocol
was from before X-Forwarded-Proto
being the agreed standard. That would probably explain why some examples have "https" as a value and others have "ssl", there's no standard.
Makes sense!
I guess there's not much to do here except maybe keep the issue open for a while for people who are upgrading from 19.7.1 and who happen to have a similar setup to mine. It seems Django used to state setting X-Forwarded-Protocol
to https
before Django 1.6 and I think that's why it was in my nginx config.
Would it be possible to expand on error message to say something like "X-Forwarded-Protocol was set to 'https' but that doesn't match the expected value in secure_scheme_headers
"? I had no idea what "Contradictory scheme headers" meant when I first saw it (though, it makes sense in retrospect).
We could see about rewriting that code to show all the headers and their values. I'm happy to review pull requests for that!
Saying that the value doesn't match the expected value isn't quite enough information. It's okay if a value doesn't match, that is the expected case when a client connects without SSL. What the error message means is that some header matches and some header does not, which is what it's a contradiction. Some header says the connection is secure and some other header says the connection is not secure.
We could add a note about X-Forwarded-Protocol
to our nginx configuration example:
https://github.com/benoitc/gunicorn/blob/master/examples/nginx.conf
We already have a couple notes and hints on how to configure nginx with Gunicorn in the file.
Yeah, my first version of that comment had the value, but then I was wondering if that might be a security issue leaking that info. I guess it shouldn't be an issue if the value is hard coded in the proxy or otherwise unmodifiable by the end user.
@berkerpeksag - I think the only issue is people with legacy configurations as no one really should be setting X-Forwarded-Protocol
unless they're using old libraries that haven't adopted to the standard.
We might be fine to leave things as is. Our example nginx configuration should work. If people search for "Contradictory secure scheme headers" they'll land of one of the issues here and figure out what's going on.
maybe adding a short note on our configuration file or in the deploying doc would worth it. Not sure about the phrasing. Any idea?
bump
Either way. If someone wants to write it, I would review the PR, but I'm comfortable with the current documentation.
What about changing the error message from "Contradictory scheme headers" to something more verbose?
If you'd like to make a PR, I would be happy to review it.
gunicorn.http.errors
has a InvalidSchemeHeaders
exception that right now has no arguments. It is created in gunicorn.http.message
, where the conflicting values should be available to you.
If you nginx and app not on one host, probably, this solution: https://docs.gunicorn.org/en/stable/settings.html#forwarded-allow-ips
Thanks for the discussion everyone! As this has now been open for a while with no action, I'm going to close it. I will still happily review any documentation PRs.
If you are unable to change the nginx configuration, there is one more way to work around this problem.
Create config file (for example: gunicorn.py
):
secure_scheme_headers = {'X-FORWARDED-PROTOCOL': 'https', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
Then run gunicorn with this configuration:
gunicorn --config /path/to/gunicorn.py
Then everything will work fine without changing the proxy.
I hope I helped someone :wink: :beers:
You're correct about the cause. The value for
X-Forwarded-Protocol
should bessl
but$scheme
will behttp
orhttps
. You can either change your nginx configuration to setX-Forwarded-Protocol
tossl
conditionally, based on the value of$scheme
; changesecure_scheme_headers
; or removeproxy_set_header X-Forwarded-Protocol
altogether.I'm not familiar with any software that sets
X-Forwarded-Protocol
(onlyX-Forwarded-Proto
), so I can't say whether the defaultsecure_scheme_headers
are correct.I don't see any bug in Gunicorn. Thankfully, this seems like it could be solved with configuration changes.
Hi @tilgovi , I saw mozilla / aws / nginx's documents said that, x-forwarded-proto
should be https instead of ssl.
Most helpful comment
You're correct about the cause. The value for
X-Forwarded-Protocol
should bessl
but$scheme
will behttp
orhttps
. You can either change your nginx configuration to setX-Forwarded-Protocol
tossl
conditionally, based on the value of$scheme
; changesecure_scheme_headers
; or removeproxy_set_header X-Forwarded-Protocol
altogether.I'm not familiar with any software that sets
X-Forwarded-Protocol
(onlyX-Forwarded-Proto
), so I can't say whether the defaultsecure_scheme_headers
are correct.I don't see any bug in Gunicorn. Thankfully, this seems like it could be solved with configuration changes.