diff --git a/gettingstarted/settings.py b/gettingstarted/settings.py index 6ed26f4fa..e5228022f 100644 --- a/gettingstarted/settings.py +++ b/gettingstarted/settings.py @@ -49,14 +49,26 @@ # The `DYNO` env var is set on Heroku CI, but it's not a real Heroku app, so we have to # also explicitly exclude CI: # https://devcenter.heroku.com/articles/heroku-ci#immutable-environment-variables -IS_HEROKU_APP = "DYNO" in os.environ and not "CI" in os.environ +IS_HEROKU_APP = "DYNO" in os.environ and "CI" not in os.environ -# On Heroku, it's safe to use a wildcard for `ALLOWED_HOSTS`, since the Heroku router performs -# validation of the Host header in the incoming HTTP request. On other platforms you may need to -# list the expected hostnames explicitly in production to prevent HTTP Host header attacks. See: -# https://docs.djangoproject.com/en/5.1/ref/settings/#std-setting-ALLOWED_HOSTS if IS_HEROKU_APP: + # On Heroku, it's safe to use a wildcard for `ALLOWED_HOSTS`, since the Heroku router performs + # validation of the Host header in the incoming HTTP request. On other platforms you may need to + # list the expected hostnames explicitly in production to prevent HTTP Host header attacks. See: + # https://docs.djangoproject.com/en/5.1/ref/settings/#std-setting-ALLOWED_HOSTS ALLOWED_HOSTS = ["*"] + + # Redirect all non-HTTPS requests to HTTPS. This requires that: + # 1. Your app has a TLS/SSL certificate, which all `*.herokuapp.com` domains do by default. + # When using a custom domain, you must configure one. See: + # https://devcenter.heroku.com/articles/automated-certificate-management + # 2. Your app's WSGI web server is configured to use the `X-Forwarded-Proto` headers set by + # the Heroku Router (otherwise you may encounter infinite HTTP 301 redirects). See this + # app's `gunicorn.conf.py` for how this is done when using gunicorn. + # + # For maximum security, consider enabling HTTP Strict Transport Security (HSTS) headers too: + # https://docs.djangoproject.com/en/5.1/ref/middleware/#http-strict-transport-security + SECURE_SSL_REDIRECT = True else: ALLOWED_HOSTS = [".localhost", "127.0.0.1", "[::1]", "0.0.0.0", "[::]"] diff --git a/gunicorn.conf.py b/gunicorn.conf.py index f17967105..09e78a5d6 100644 --- a/gunicorn.conf.py +++ b/gunicorn.conf.py @@ -1,6 +1,7 @@ # Gunicorn configuration file: # https://docs.gunicorn.org/en/stable/configure.html # https://docs.gunicorn.org/en/stable/settings.html +# # Note: The classic Python buildpack currently sets a few gunicorn settings automatically via # the `GUNICORN_CMD_ARGS` env var (which take priority over the settings in this file): # https://github.com/heroku/heroku-buildpack-python/blob/main/vendor/python.gunicorn.sh @@ -70,3 +71,9 @@ # duplicate gunicorn processes have accidentally been launched (eg in different # terminals), since the "address already in use" error no longer occurs. reuse_port = True + + # Trust the `X-Forwarded-Proto` header set by the Heroku Router during TLS termination, + # (https://devcenter.heroku.com/articles/http-routing#heroku-headers) so that HTTPS requests + # are correctly marked as secure. This allows the WSGI app (in our case, Django) to distinguish + # between HTTP and HTTPS requests for features like HTTP->HTTPS URL redirection. + forwarded_allow_ips = "*"