Skip to content

Flask - Redirect & Errors

Redirects are a fundamental part of web applications. They are used to send the user’s browser from one URL to another. Common use cases include:

  • Redirecting after successful form submission (e.g., from a login form to the user’s dashboard).
  • Redirecting after an action (e.g., after deleting an item, redirect back to the list view).
  • Directing users from an old URL to a new one.
  • Enforcing canonical URLs (e.g., adding or removing a trailing slash).

Flask provides the redirect() function to generate redirect responses.

from flask import redirect, url_for
# Inside a view function:
def process_action():
# ... perform some action ...
# Redirect to the 'show_results' endpoint
return redirect(url_for('show_results'))

Parameters:

  • location: The URL to redirect the user to. It is strongly recommended to generate this URL using url_for() rather than hardcoding paths.
  • code=302: The HTTP status code for the redirect. Defaults to 302 Found (temporary redirect).
  • Response=None: An optional custom Response class to use.

Common Redirect Status Codes:

  • 301 Moved Permanently: Use when a resource has permanently moved to a new location. Search engines typically update their links.
  • 302 Found: Default. Indicates a temporary redirect. Clients should continue using the original URL for future requests.
  • 303 See Other: Often used after a POST request to redirect the client to a different URL (usually a success page) using a GET request. Helps prevent accidental resubmission if the user refreshes.
  • 307 Temporary Redirect: Similar to 302, but instructs the client to reuse the same HTTP method for the new request (e.g., if the original was POST, the redirect request should also be POST).

Example: Redirecting after Login Attempt

from flask import Flask, redirect, url_for, render_template, request, flash
app = Flask(__name__)
# Required for flash messages
app.secret_key = 'redirect-example-secret'
@app.route('/login-redir')
def login_form():
# Renders templates/login_redir.html
return render_template('login_redir.html')
@app.route('/process-login-redir', methods=['POST'])
def process_login():
# Simulate checking credentials
if request.form.get('username') == 'admin':
flash('Login successful!', 'success')
# Redirect to success page using url_for()
return redirect(url_for('success_page'))
else:
flash('Invalid username.', 'error')
# Failed login, redirect back to the login form
return redirect(url_for('login_form'))
@app.route('/login-success')
def success_page():
return 'Logged in successfully!'
# Assumed template: templates/login_redir.html
# <!DOCTYPE html><html><head><title>Login</title></head><body>
# {% with messages = get_flashed_messages(with_categories=true) %}
# {% if messages %}
# {% for category, message in messages %}<p class="{{category}}">{{ message }}</p>{% endfor %}
# {% endif %}
# {% endwith %}
# <form method="post" action="{{ url_for('process_login') }}">
# Username: <input type="text" name="username"><br>
# <input type="submit" value="Login">
# </form></body></html>
if __name__ == '__main__':
app.run(debug=True, port=5008)

Sometimes, processing a request cannot continue normally due to an error condition (e.g., resource not found, user not authorized, invalid input). In such cases, you should signal an appropriate HTTP error status. Flask provides the abort() function for this.

abort() immediately stops the execution of the current view function and raises an HTTPException for the given status code. Flask catches this exception and returns the corresponding error page to the client.

from flask import abort, session
@app.route('/product/<int:product_id>')
def get_product(product_id):
product = find_product_in_db(product_id) # Assume this function exists
if product is None:
# Product not found, abort with a 404 error
abort(404)
return render_template('product_details.html', product=product)
@app.route('/admin/config')
def admin_config():
if not session.get('is_admin'):
# User is not authorized, abort with a 403 error
abort(403)
return 'Admin Configuration Page'

Common Error Status Codes for abort():

  • 400 Bad Request: Invalid request syntax or parameters.
  • 401 Unauthorized: Authentication is required but missing or failed.
  • 403 Forbidden: Authenticated user lacks permission to access the resource.
  • 404 Not Found: The requested resource could not be found.
  • 405 Method Not Allowed: The HTTP method used (e.g., POST) is not supported for this URL.
  • 410 Gone: The resource used to exist but is permanently unavailable.
  • 500 Internal Server Error: A generic code for unexpected server errors.

You can also pass a description argument to abort():

abort(400, description="Missing 'user_id' parameter in request.")

Instead of showing Flask’s default error pages, you can create custom, user-friendly templates for specific HTTP error codes using the @errorhandler() decorator.

from flask import render_template
@app.errorhandler(404)
def handle_not_found_error(error):
# Log the error if needed: app.logger.warning(f'Page not found: {request.path}')
# The 'error' argument contains information about the exception
return render_template('errors/404.html'), 404 # Return template and status code
@app.errorhandler(403)
def handle_forbidden_error(error):
return render_template('errors/403.html'), 403
@app.errorhandler(500)
def handle_internal_error(error):
# It's often good practice to rollback database sessions here if applicable
# db.session.rollback()
app.logger.error(f'Server Error: {error}', exc_info=True)
return render_template('errors/500.html'), 500
# --- Assumed Templates ---
# templates/errors/404.html: "Sorry, the page you requested was not found."
# templates/errors/403.html: "Sorry, you do not have permission to access this page."
# templates/errors/500.html: "Sorry, something went wrong on our end. Please try again later."
# ------------------------

When an error corresponding to a registered handler occurs (either through abort() or an unhandled exception leading to a 500 error), Flask will execute the associated error handler function instead of its default behavior.