URL Building

Difficulty: Beginner

URL building with url_for() is one of Flask's most important features. Instead of hardcoding URLs throughout your templates and code, url_for() generates URLs dynamically based on the endpoint name. This makes your application resilient to URL changes and prevents broken links.

The url_for() function takes an endpoint name as its first argument and any number of keyword arguments for URL variables. It returns the URL path as a string. If the route has variable rules, you must pass the corresponding values as keyword arguments. Any keyword arguments that do not match URL variables are appended as query string parameters.

Using url_for() has several advantages over hardcoded URLs. First, if you change a URL pattern, all url_for() calls automatically generate the correct new URL. Second, url_for() properly escapes special characters in URLs. Third, it handles the application's root path (SCRIPT_NAME) correctly when your app is mounted at a sub-path behind a reverse proxy. Fourth, it generates absolute URLs when needed (useful for emails and API responses).

To generate absolute URLs (including the scheme and host), pass _external=True to url_for(). This produces URLs like 'http://localhost:5000/about' instead of just '/about'. This is essential when generating URLs for email content, API responses, or any context where the URL will be used outside the browser's relative URL resolution.

url_for() also handles static files through the special 'static' endpoint. Flask automatically registers a route for serving files from the 'static' directory. You generate URLs to static files with url_for('static', filename='css/style.css'), which produces '/static/css/style.css'. In templates, this is how you link to CSS, JavaScript, and image files.

The redirect() function works hand-in-hand with url_for(). When you need to send the user to a different page (after form submission, after login, etc.), you call redirect(url_for('endpoint_name')). This sends a 302 redirect response. You can also specify a different status code like 301 for permanent redirects.

In Jinja2 templates, url_for() is available as a global function. You use it in href attributes, form actions, script src attributes, and anywhere else you need URLs. The pattern {{ url_for('endpoint', key=value) }} is used extensively in Flask templates.

url_for() raises a BuildError if the endpoint does not exist or if required URL variables are missing. This is actually a feature: it catches broken links at render time rather than letting them fail silently. In production, you should handle BuildError for user-generated content that might reference invalid endpoints.

Code examples

Basic url_for() Usage

from flask import Flask, url_for, redirect

app = Flask(__name__)

@app.route('/')
def index():
    return f'''
        <a href="{url_for('about')}">About</a>
        <a href="{url_for('profile', username='alice')}">Alice</a>
        <a href="{url_for('search', q='flask', page=1)}">Search</a>
    '''

@app.route('/about')
def about():
    return 'About page'

@app.route('/user/<username>')
def profile(username):
    return f'Profile: {username}'

@app.route('/search')
def search():
    return 'Search results'

url_for('about') generates '/about'. url_for('profile', username='alice') fills the variable rule to produce '/user/alice'. Extra keyword arguments like q and page are appended as query parameters.

Static File URLs and External URLs

from flask import Flask, url_for

app = Flask(__name__)

@app.route('/')
def index():
    # Static file URLs
    css = url_for('static', filename='css/style.css')
    js = url_for('static', filename='js/app.js')
    img = url_for('static', filename='images/logo.png')

    # External (absolute) URL
    ext = url_for('index', _external=True)

    return f'''
        <link rel="stylesheet" href="{css}">
        <img src="{img}" alt="Logo">
        <p>Absolute URL: {ext}</p>
        <script src="{js}"></script>
    '''

The 'static' endpoint is automatically registered. url_for('static', filename='...') generates paths to your static directory. The _external=True parameter generates full absolute URLs including scheme and host.

Redirects with url_for()

from flask import Flask, url_for, redirect, request

app = Flask(__name__)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # After successful login, redirect to dashboard
        return redirect(url_for('dashboard'))
    return '<form method="POST"><button>Login</button></form>'

@app.route('/dashboard')
def dashboard():
    return 'Welcome to your dashboard!'

@app.route('/old-page')
def old_page():
    # Permanent redirect (301) to new page
    return redirect(url_for('new_page'), code=301)

@app.route('/new-page')
def new_page():
    return 'This is the new page'

redirect() sends a 302 (temporary) redirect by default. Use code=301 for permanent redirects. Always combine redirect() with url_for() instead of hardcoding the target URL.

url_for() in Jinja2 Templates

<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">Home</a>
        <a href="{{ url_for('about') }}">About</a>
        <a href="{{ url_for('profile', username='me') }}">Profile</a>
    </nav>

    {% block content %}{% endblock %}

    <script src="{{ url_for('static', filename='js/app.js') }}"></script>
</body>
</html>

url_for() is available in Jinja2 templates as a global function. Use it for all links, form actions, static file references, and any URL. The double curly braces {{ }} output the result into the HTML.

Key points

Concepts covered

url_for, Static Files, External URLs, Redirect, Anchor