diff --git a/doc/htdocs/configure.html b/doc/htdocs/configure.html
index 498220eb..f7df8dfa 100644
--- a/doc/htdocs/configure.html
+++ b/doc/htdocs/configure.html
@@ -336,6 +336,29 @@ int(value, 0) (0 means Python guesses the base, so values like "0",
This path should be writable by the process permissions set for Gunicorn
workers. If not specified, Gunicorn will choose a system generated
temporary directory.
+
+
@@ -549,28 +572,29 @@ the just-exited Worker.
group
umask
tmp_upload_dir
+
secure_scheme_headers
-
Logging
-- logfile
-- loglevel
-- logconfig
+- Logging
-- Process Naming
-- proc_name
-- default_proc_name
+- Process Naming
-- Server Hooks
diff --git a/gunicorn/config.py b/gunicorn/config.py
index d1f4a73e..6f156ef9 100644
--- a/gunicorn/config.py
+++ b/gunicorn/config.py
@@ -179,6 +179,11 @@ def validate_bool(val):
else:
raise ValueError("Invalid boolean: %s" % val)
+def validate_dict(val):
+ if not isinstance(val, dict):
+ raise TypeError("Value is not a dictionary: %s " % val)
+ return val
+
def validate_pos_int(val):
if not isinstance(val, (types.IntType, types.LongType)):
val = int(val, 0)
@@ -522,6 +527,30 @@ class TmpUploadDir(Setting):
temporary directory.
"""
+class SecureSchemeHeader(Setting):
+ name = "secure_scheme_headers"
+ section = "Server Mechanics"
+ validator = validate_dict
+ default = {
+ "X-FORWARDED-PROTOCOL": "ssl",
+ "X-FORWARDED-SSL": "on"
+ }
+ desc = """\
+
+ A dictionary containing headers and values that the front-end proxy
+ uses to indicate HTTPS requests. These tell gunicorn to set
+ wsgi.url_scheme to "https", so your application can tell that the
+ request is secure.
+
+ The dictionary should map upper-case header names to exact string
+ values. The value comparisons are case-sensitive, unlike the header
+ names, so make sure they're exactly what your front-end proxy sends
+ when handling HTTPS requests.
+
+ It is important that your front-end proxy configuration ensures that
+ the headers defined here can not be passed directly from the client.
+ """
+
class Logfile(Setting):
name = "logfile"
section = "Logging"
diff --git a/gunicorn/http/wsgi.py b/gunicorn/http/wsgi.py
index a25ee31b..313a7adf 100644
--- a/gunicorn/http/wsgi.py
+++ b/gunicorn/http/wsgi.py
@@ -68,6 +68,8 @@ def create(req, sock, client, server, cfg):
url_scheme = "http"
script_name = os.environ.get("SCRIPT_NAME", "")
+ secure_headers = getattr(cfg, "secure_scheme_headers")
+
for hdr_name, hdr_value in req.headers:
if hdr_name == "EXPECT":
# handle expect
@@ -75,9 +77,8 @@ def create(req, sock, client, server, cfg):
sock.send("HTTP/1.1 100 Continue\r\n\r\n")
elif hdr_name == "X-FORWARDED-FOR":
forward = hdr_value
- elif hdr_name == "X-FORWARDED-PROTOCOL" and hdr_value.lower() == "ssl":
- url_scheme = "https"
- elif hdr_name == "X-FORWARDED-SSL" and hdr_value.lower() == "on":
+ elif (hdr_name.upper() in secure_headers and
+ hdr_value == secure_headers[hdr_name.upper()]):
url_scheme = "https"
elif hdr_name == "HOST":
server = hdr_value