python
39 lines · 7 steps
Enforcing HTTPS behind a proxy in Flask
A before_request hook redirects insecure traffic to HTTPS and an after_request hook stamps every secure response with HSTS.
Explained by
highlit
1from flask import Flask, request, redirect, current_app
2from werkzeug.middleware.proxy_fix import ProxyFix
3
4app = Flask(__name__)
5app.config.setdefault("FORCE_HTTPS", not app.debug)
6
7app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
8
9
10@app.before_request
11def enforce_https():
12 if not current_app.config["FORCE_HTTPS"]:
13 return None
14
15 if request.is_secure:
16 return None
17
18 if request.headers.get("X-Forwarded-Proto", "http") == "https":
19 return None
20
21 if request.method not in ("GET", "HEAD"):
22 return (
23 "HTTPS is required for this request.",
24 403,
25 {"Content-Type": "text/plain"},
26 )
27
28 secure_url = request.url.replace("http://", "https://", 1)
29 return redirect(secure_url, code=301)
30
31
32@app.after_request
33def set_hsts(response):
34 if current_app.config["FORCE_HTTPS"] and request.is_secure:
35 response.headers.setdefault(
36 "Strict-Transport-Security",
37 "max-age=31536000; includeSubDomains",
38 )
39 return response
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1When Flask sits behind a proxy, ProxyFix is what lets request.is_secure reflect the real client protocol.
- 2Redirect safe methods to HTTPS but reject unsafe ones outright, since a 301 can't preserve a POST body.
- 3HSTS should only be sent over already-secure connections, so the browser learns to force HTTPS itself.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
go
package middleware import ( "net/http"
Per-plan export limits in Gin middleware
middleware
rate-limiting
authorization
Intermediate
7 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/enforcing-https-behind-a-proxy-in-flask-explained-python-96dd/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.