4

I'm working on a project that uses Google Cloud Platform's App Engine in the Python 3 Flexible Environment using Django, and I'm trying to permanently redirect all requests over http to https for all routes, but so far have not been successful. I can access the site over https, but only if explicitly written in the address bar.

I've looked at this post: How to permanently redirect `http://` and `www.` URLs to `https://`? but did not find the answer useful.

The app works properly in every sense except for the redirecting. Here is my app.yaml file:

# [START runtime]
runtime: python
env: flex
entrypoint: gunicorn -b :$PORT myproject.wsgi

runtime_config:
  python_version: 3
# [END runtime]

In myproject/settings.py I have these variables defined:

SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_PROXY_SSL_HEADER = ('HTTP-X-FORWARDED-PROTO', 'https')

On my local machine, when I set SECURE_SSL_REDIRECT to True, I was redirected to https properly, even though SSL is not supported on localhost. In production, I am still able to access the site using just http.

Is there something I'm missing or doing wrong to cause the redirect not to happen?

Dustin Ingram
  • 20,502
  • 7
  • 59
  • 82
Henry Woody
  • 14,024
  • 7
  • 39
  • 56

5 Answers5

7

Setting secure in app.yaml only works for GAE Standard but not in Flexible. The app.yaml docs for Flexible do not mention this key at all.

You will probably have to do it on application level by inspecting the value of the X-Forwarded-Proto header. It will be set to https if the request to your app came by HTTPS. You can find more info on environment-provided headers in Flexible environment in the docs here.

Mikhail Burshteyn
  • 4,762
  • 14
  • 27
1

Make sure you have SecurityMiddleware and CommonMiddleware enabled, and assign a Base_URL:

settings.py:

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.security.SecurityMiddleware'
    'django.middleware.common.CommonMiddleware',    
)

BASE_URL = 'https://www.example.com'

Or, you could write your own middleware:

MIDDLEWARE_CLASSES = (
    ...
    'core.my_middleware.ForceHttps',
)

BASE_URL = 'https://www.example.com'

my_middleware.py:

from django.http import HttpResponsePermanentRedirect

class ForceHttps(object):

    def process_request(self, request):

        if not (request.META.get('HTTPS') == 'on' and settings. BASE_URL == 'https://' + request.META.get('HTTP_HOST') ):
            return HttpResponsePermanentRedirect(settings. BASE_URL + request.META.get('PATH_INFO'))
        else:
            return None
GAEfan
  • 11,244
  • 2
  • 17
  • 33
1

The issue is the header name. When accessing Django through a WSGI server, you should use the X-Forwarded-Proto header instead of the HTTP_X_FORWARDED_PROTO.

See: Why does django ignore HTTP_X_FORWARDED_PROTO from the wire but not in tests?

Dustin Ingram
  • 20,502
  • 7
  • 59
  • 82
1

I had a similar problem and tried a number changes both in the app.yaml and also in settings.py for a custom domain (with the default ssl cert supplied by GAE).

Through trial and error I found that in settings.py updating the allowed hosts to the appropriate domains had the desired result:

ALLOWED_HOSTS = ['https://{your-project-name}.appspot.com','https://www.yourcustomdomain.com'] 

Update: I am no longer sure the above is the reason as on a subsequent deploy the above was rejected and I was getting a hosts error. However the redirect is still in place... :(

Before this change I was able to switch between http:// and https:// manually in the address bar now it redirects automaticlly.

soupcoder
  • 11
  • 2
1

In order to make this work both on App Engine Flexible and your local machine when testing, you should set the following in your settings.py

if os.getenv('GAE_INSTANCE'):
    SECURE_SSL_REDIRECT = True
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
else:
    # Running on your local machine
    SECURE_SSL_REDIRECT = False
    SECURE_PROXY_SSL_HEADER = None

That should be all you need to do to ensure that redirect is happening properly when you are on app engine.

NOTE: If you are using old school app engine cron jobs (via cron.yaml) then you will need to start using the much improved cloud scheduler instead. App engine cron jobs do not properly support redirection to HTTPS but you can easily get it working with cloud scheduler.

NateB80
  • 200
  • 2
  • 8
  • 1
    For what it's worth, I don't think there's any harm in setting `SECURE_PROXY_SSL_HEADER` regardless of whether you have `SECURE_SSL_REDIRECT = True`, so instead of an `if/else` statement I would suggest `SECURE_SSL_REDIRECT = bool(os.getenv('GAE_INSTANCE'))'` – DragonBobZ Aug 13 '21 at 15:00
  • @DragonBobZ that looks correct, I haven't tested that out yet but that should work great. Thanks! – NateB80 Sep 07 '21 at 00:21