The Request Object
Introduction
Every HTTP request arrives as Flask’s request proxy—query strings, form fields, JSON bodies, headers, cookies, and client metadata. This chapter shows how to read each part safely and notes proxy headers when your app sits behind Nginx.
Prerequisites
- Routing and URLs
- Basic HTTP concepts (GET, POST, headers)
Import and Basics
from flask import Flask, request
app = Flask(__name__)
@app.route("/debug-request", methods=["GET", "POST"])
def debug_request():
return {
"method": request.method,
"path": request.path,
"content_type": request.content_type,
}Code explanation:
requestis thread-local—safe in sync views under WSGI- Only valid inside a request context (view,
before_request, etc.)
Query String: request.args
URL: /search?q=flask&page=2
@app.route("/search")
def search():
q = request.args.get("q", "")
page = request.args.get("page", 1, type=int)
return f"Search: {q}, page {page}"Code explanation:
request.argsis like a dict of query parameterstype=intconverts or returnsNoneif invalid
Multiple values:
tags = request.args.getlist("tag")Form Data: request.form
HTML form POST:
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username", "")
password = request.form.get("password", "")
return f"Hello, {username}"
return '<form method="post"><input name="username"><button>Go</button></form>'Use request.form for application/x-www-form-urlencoded and multipart/form-data (non-file fields).
JSON Body: request.get_json()
Client sends Content-Type: application/json:
@app.route("/api/items", methods=["POST"])
def create_item():
data = request.get_json(silent=True)
if not data:
return {"error": "JSON required"}, 400
name = data.get("name")
return {"created": name}, 201Test:
curl -X POST http://127.0.0.1:5000/api/items \
-H "Content-Type: application/json" \
-d '{"name":"widget"}'Code explanation:
silent=Trueavoids exception on bad JSON—returnsNoneforce=Trueparses body even without JSON content type (use carefully)
Headers and Cookies
@app.route("/headers-demo")
def headers_demo():
user_agent = request.headers.get("User-Agent", "")
auth = request.headers.get("Authorization")
session_id = request.cookies.get("session_id")
return {"ua": user_agent, "auth": bool(auth), "session": session_id}Custom headers for APIs:
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:5000/headers-demoFiles: request.files (Preview)
from flask import Flask, request
@app.route("/upload", methods=["POST"])
def upload():
file = request.files.get("photo")
if not file or file.filename == "":
return "No file", 400
return f"Received {file.filename}"Full upload chapter covers validation and storage.
Client IP and Proxies
@app.route("/who")
def who():
return {"remote_addr": request.remote_addr}Behind Nginx, remote_addr may be 127.0.0.1. Trust proxy config:
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)Then read request.access_route or configured forwarded headers—only when you control the proxy.
Tip
Trust Proxies Deliberately
Misconfigured ProxyFix lets clients spoof IP—enable only behind your load balancer.
Request vs Global State
Do not store request data in global variables across requests—use g (later chapter) for per-request app state.
FAQ
request.form empty on POST?
Check method="post", Content-Type, and that you read form not json.
get_json returns None?
Missing body, wrong content type, or invalid JSON—inspect with request.data.
Unicode in query args?
Flask decodes UTF-8 query strings—ensure client encodes properly.
CSRF on forms?
Raw HTML forms need CSRF tokens—Flask-WTF chapter covers this.
Large JSON payloads?
Configure MAX_CONTENT_LENGTH in config to reject huge bodies.
request.json vs get_json?
request.json property may raise; get_json(silent=True) is safer in APIs.