Flask Request Lifecycle Hooks Explained
In Python's Flask framework, the execution lifecycle of an HTTP
request is managed through decorator-based hooks that execute code
before, after, or at the conclusion of a route handler. Understanding
how @app.before_request, @app.after_request,
and @app.teardown_request interact is essential for
handling tasks like authentication, response header modification,
database connection pooling, and error cleanup cleanly without
cluttering individual view functions.
The Request Lifecycle Flow
When an HTTP request hits a Flask application, it follows a deterministic path:
- The Flask application creates a request context.
@app.before_requestfunctions execute.- The matched route view function processes the request (if not short-circuited).
@app.after_requestfunctions modify the generated response.@app.teardown_requestfunctions execute to clean up resources.- The response is returned to the client, and the request context is torn down.
1. @app.before_request
The before_request hook executes before every request
enters its corresponding view function.
- Primary Use Cases: User authentication, session
validation, establishing database transactions, loading globally
required data into Flask's
gobject, and request logging. - Short-Circuiting Behavior: If a
before_requestfunction returns a value (such as a redirect, an error message, or an abort), Flask halts further execution immediately. The intended view function is bypassed, and the returned value is converted into a response and passed directly down the lifecycle. - Execution Order: If multiple
before_requestfunctions are registered, they run in the order they were defined.
from flask import Flask, g, request, abort
app = Flask(__name__)
@app.before_request
def authenticate_user():
token = request.headers.get("Authorization")
if not token and request.endpoint != "login":
abort(401)
g.user = {"id": 1, "name": "Alice"}2. @app.after_request
The after_request hook executes after the view function
successfully produces a response, but before that response is sent to
the client.
- Primary Use Cases: Adding security headers, injecting CORS headers, modifying cookies, compressing payloads, and recording response metrics.
- Requirements: Functions decorated with
after_requestmust accept exactly one argument—theresponseobject—and must return a validresponseobject (either the modified original or a new one). - Exception Sensitivity: This hook runs
only if the request completed without an unhandled
exception. If an unhandled exception occurs inside a view or a
before_requestfunction,after_requesthooks are skipped entirely. - Execution Order: If multiple
after_requestfunctions are registered, they execute in reverse order of their registration (LIFO: Last-In, First-Out).
@app.after_request
def add_security_headers(response):
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
return response3.
@app.teardown_request
The teardown_request hook executes at the very end of
the request context lifecycle, when the request context is being
popped.
- Primary Use Cases: Closing database connections, releasing locks, terminating third-party network sessions, and cleaning up temporary files.
- Guaranteed Execution: Unlike
after_request,teardown_requestis always invoked, even if an unhandled exception occurs during the execution of abefore_requesthook or the view function. - Arguments: Functions decorated with
teardown_requestmust accept an optionalexceptionparameter. If an unhandled exception caused the request to fail, that exception object is passed into the function; otherwise, it receivesNone. - Limitations: Teardown handlers cannot alter the HTTP response sent to the client because the response has already been finalized or generated by an error handler.
- Execution Order: Like
after_request, teardown handlers execute in reverse order of registration.
@app.teardown_request
def close_db_connection(exception=None):
db = getattr(g, "_database", None)
if db is not None:
db.close()Comparison of Lifecycle Hooks
| Feature | before_request |
after_request |
teardown_request |
|---|---|---|---|
| Execution Point | Before route handler | After route handler | When context is destroyed |
| Input Arguments | None | response |
exception=None |
| Must Return | None (or Response to halt) | response |
Nothing (ignored) |
| Runs on Error? | No | No | Yes (guaranteed) |
| Execution Order | Registration order | Reverse order | Reverse order |