Response Object

Difficulty: Intermediate

The Response object in Flask represents the HTTP response that gets sent back to the client. While Flask automatically converts view function return values into responses, understanding the Response object gives you full control over status codes, headers, cookies, and the response body.

Flask view functions can return several types that are automatically converted to Response objects. A string becomes an HTML response with status 200. A dict is converted to a JSON response via jsonify(). A tuple of (body, status) or (body, status, headers) gives you control over the status code and headers. A Response object is returned as-is.

The make_response() function creates a Response object from the same types of values you would return from a view. It is useful when you need to modify the response before returning it, such as setting cookies, custom headers, or cache control directives.

The jsonify() function creates a JSON response with the correct Content-Type header (application/json). While returning a dict from a view also creates JSON, jsonify() offers more flexibility: it accepts keyword arguments, handles top-level arrays, and sets proper headers. In Flask 2.2+, returning a dict is effectively equivalent to jsonify().

HTTP status codes communicate the result of a request. Common codes include 200 (OK), 201 (Created), 204 (No Content), 301 (Moved Permanently), 302 (Found/Redirect), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 404 (Not Found), 405 (Method Not Allowed), and 500 (Internal Server Error). Flask provides abort() to immediately terminate a request with an error status.

Custom response headers let you control browser behavior. Common headers include Cache-Control (caching), Content-Disposition (file downloads), X-Content-Type-Options (MIME sniffing prevention), and Access-Control-Allow-Origin (CORS). You set them on the response object or via the after_request hook.

For large responses, Flask supports streaming. Instead of building the entire response in memory, you can return a generator function. Flask sends chunks as they are produced, keeping memory usage low. The stream_with_context() wrapper is needed if your generator accesses the request context.

Flask's after_request and teardown_request hooks let you modify every response. after_request runs after each request and receives the response object, letting you add headers, log responses, or modify content. teardown_request runs even if an error occurred and is used for cleanup.

Code examples

Creating Responses with make_response

from flask import Flask, make_response

app = Flask(__name__)

@app.route('/custom')
def custom_response():
    resp = make_response('Hello, World!')
    resp.status_code = 200
    resp.headers['X-Custom-Header'] = 'MyValue'
    resp.headers['Cache-Control'] = 'no-cache, no-store'
    return resp

@app.route('/xml')
def xml_response():
    xml = '<?xml version="1.0"?><data><item>Flask</item></data>'
    resp = make_response(xml)
    resp.headers['Content-Type'] = 'application/xml'
    return resp

@app.route('/download')
def download():
    data = 'name,age\nAlice,30\nBob,25'
    resp = make_response(data)
    resp.headers['Content-Type'] = 'text/csv'
    resp.headers['Content-Disposition'] = 'attachment; filename=data.csv'
    return resp

make_response() creates a Response object you can customize. Set headers, status codes, content types, and more. The download example triggers a file download in the browser using Content-Disposition.

Using jsonify and abort

from flask import Flask, jsonify, abort

app = Flask(__name__)

users = [
    {'id': 1, 'name': 'Alice'},
    {'id': 2, 'name': 'Bob'},
]

@app.route('/api/users')
def list_users():
    return jsonify(users)  # Top-level array

@app.route('/api/users/<int:user_id>')
def get_user(user_id):
    user = next((u for u in users if u['id'] == user_id), None)
    if user is None:
        abort(404)  # Immediately returns 404 error
    return jsonify(user)

@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Not found'}), 404

jsonify() handles arrays and sets Content-Type to application/json. abort() immediately stops the request with an error status. @app.errorhandler customizes the error response format.

Error Handlers

from flask import Flask, jsonify

app = Flask(__name__)

@app.errorhandler(400)
def bad_request(error):
    return jsonify({'error': 'Bad request', 'message': str(error)}), 400

@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Not found'}), 404

@app.errorhandler(500)
def server_error(error):
    return jsonify({'error': 'Internal server error'}), 500

# Handle specific exceptions
@app.errorhandler(ValueError)
def handle_value_error(error):
    return jsonify({'error': str(error)}), 400

@app.route('/api/divide/<int:a>/<int:b>')
def divide(a, b):
    if b == 0:
        raise ValueError('Division by zero')
    return {'result': a / b}

Error handlers customize error responses for specific status codes or exception types. This lets you return consistent JSON error responses instead of Flask's default HTML error pages.

after_request Hook

from flask import Flask

app = Flask(__name__)

@app.after_request
def add_security_headers(response):
    """Add security headers to every response."""
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    return response

@app.after_request
def add_cors_headers(response):
    """Add CORS headers for API access."""
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
    return response

@app.route('/api/data')
def data():
    return {'value': 42}

after_request runs after every request and receives the response object. It is perfect for adding security headers, CORS headers, or logging. The function must return the response object.

Key points

Concepts covered

make_response, jsonify, Response, Status Codes, Headers, Streaming