Request Lifecycle Hooks in Flask
Flask exposes hooks that run at defined points in the request/response cycle: before_request runs before the view function, after_request runs after a successful view (and can modify the response), teardown_request always runs even if an exception occurred, and teardown_appcontext runs when the application context is popped. These hooks are commonly used for tasks like opening a database connection, injecting request timing headers, or logging every request uniformly without repeating code in every view.
Cricket analogy: Before every over, the umpire checks the bowler's run-up mark and field placements are legal, a standardized check that runs regardless of which bowler is up, just as before_request runs before every view function uniformly.
after_request, teardown_request, and Response Modification
after_request receives the response object and must return it (possibly modified), making it ideal for adding headers like X-Request-ID or Content-Security-Policy uniformly. Unlike after_request, teardown_request and teardown_appcontext are guaranteed to run even when an exception propagates out of the view, which makes them the correct place to close database sessions or release connections regardless of whether the request succeeded or failed.
Cricket analogy: The scorer updates the scoreboard with the final tally after every over concludes, always tagging it with the over number, just as after_request stamps the response with a request ID before it's sent.
import time
import uuid
from flask import Flask, g, request
app = Flask(__name__)
@app.before_request
def start_timer():
g.start_time = time.time()
g.request_id = str(uuid.uuid4())
@app.after_request
def add_headers(response):
response.headers["X-Request-ID"] = g.request_id
elapsed = time.time() - g.start_time
response.headers["X-Response-Time-ms"] = f"{elapsed * 1000:.1f}"
return response
@app.teardown_appcontext
def close_db(exception=None):
db_session = g.pop("db_session", None)
if db_session is not None:
db_session.close()
WSGI Middleware vs Flask Hooks
Flask's before/after_request hooks operate within Flask's own request context and have access to g, request, and session, whereas WSGI middleware (functions wrapping app.wsgi_app) operate at a lower level, before Flask has even built its request context, and are the right tool for concerns like reverse-proxy header handling (ProxyFix) or enforcing HTTPS redirects that need to happen before Flask-specific objects exist.
Cricket analogy: Ground security screens every spectator entering the stadium before they even reach their specific seating section, a check that happens outside the match itself, just as WSGI middleware runs before Flask's request context is even built.
werkzeug.middleware.proxy_fix.ProxyFix is commonly applied as app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1) when running behind a reverse proxy like Nginx, so request.remote_addr and request.is_secure reflect the original client, not the proxy.
before_request hooks registered on a blueprint only run for requests routed to that blueprint's endpoints, while before_request hooks registered directly on the app run for every request — mixing these up silently causes hooks to be skipped for certain routes.
- before_request runs before every view function; after_request runs after and must return the response.
- teardown_request and teardown_appcontext always run, even after an unhandled exception, making them correct for cleanup.
- The g object is the standard place to stash per-request state like timers or request IDs for hooks to share.
- WSGI middleware wraps app.wsgi_app and runs before Flask builds its request context, suited to proxy/header concerns.
- ProxyFix is the standard middleware for correctly reading client IP and scheme behind a reverse proxy.
- Blueprint-registered before_request hooks only fire for that blueprint's routes, unlike app-level hooks which fire for all requests.
- Choosing between a Flask hook and WSGI middleware depends on whether Flask-specific objects (request, g, session) are needed.
Practice what you learned
1. Which hook is guaranteed to run even if the view raises an unhandled exception?
2. What must an after_request function return?
3. What is ProxyFix used for?
4. How do blueprint-level before_request hooks differ from app-level ones?
Was this page helpful?
You May Also Like
Error Handling in Flask
How to use abort(), @app.errorhandler, and logging to turn expected HTTP errors and unexpected exceptions into consistent, safe responses.
Flask Application Factories
Why and how to build Flask apps with a create_app() factory function, enabling multiple configured instances, easier testing, and fewer circular imports.
Flask Configuration Management
Layering config classes, environment variables, and .env files to manage Flask settings and secrets safely across development, testing, and production.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics