URL Building & Redirects with url_for
Reviewed & published by Brayan K
url_for is a Flask function that builds the correct URL for a view by its name, so you never hardcode a path again.
Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
In this lesson you'll generate links from view names, attach query parameters, reference static files, and send the browser elsewhere with redirect.
What You'll Learn in This Lesson
- Build URLs from a view's name with url_for
- Pass route parameters as keyword arguments
- Why hardcoding URLs is fragile
- Append query strings with extra keywords
- Link static files via url_for('static')
- Redirect to another route with redirect(url_for(...))
1 Building URLs From View Names
url_for("view_name") asks Flask for the URL registered to a view function and returns it as a string. You pass the function's name (its endpoint), and any route parameters as keyword arguments.
Because url_for needs to know about the request, we run it inside test_request_context() — a helper that fakes a request so the example works with no server running. In a real view, the context already exists.
# app.py — build URLs by view name
from flask import Flask, url_for
app = Flask(__name__)
@app.route("/")
def home():
return "home page"
@app.route("/user/<username>")
def profile(username):
return f"Profile of {username}"
# test_request_context() lets url_for run without a live server
with app.test_request_context():
print(url_for("home")) # /
print(url_for("profile", username="ada")) # /user/ada
print(url_for("profile", username="grace")) # /user/grace
# ✅ Expected output:
# /
# /user/ada
# /user/grace2 Query Strings, Static Files & Why Not to Hardcode
Any keyword you pass that is not a route parameter becomes a query-string value. So url_for("search", q="flask", page=2) appends ?q=flask&page=2 automatically.
The built-in static endpoint generates URLs for your assets: url_for("static", filename="style.css") returns /static/style.css. If you hardcoded /static/... and later moved the folder or added a CDN prefix, every link would break — url_for stays correct.
# app.py — query strings and static files
from flask import Flask, url_for
app = Flask(__name__)
@app.route("/")
def home():
return "home"
@app.route("/search")
def search():
return "results"
@app.route("/user/<username>")
def profile(username):
return f"Profile: {username}"
with app.test_request_context():
# Extra keywords that aren't route params become a query string
print(url_for("search", q="flask", page=2)) # /search?q=flask&page=2
# Mix a route param with a query param
print(url_for("profile", username="ada", ref="email")) # /user/ada?ref=email
# The built-in 'static' endpoint for CSS/JS/images
print(url_for("static", filename="css/style.css")) # /static/css/style.css
# ✅ Expected output:
# /search?q=flask&page=2
# /user/ada?ref=email
# /static/css/style.cssIn a Jinja template you'd write the same call: . That keeps your HTML links in sync with your routes.
3 Redirecting With redirect(url_for(...))
redirect(target) tells the browser to go to another URL by sending a 302 response with a Location header. The idiomatic pattern is redirect(url_for("some_view")) so the destination is built from a name, not a hardcoded path.
This is the backbone of the Post/Redirect/Get pattern: after handling a form POST, you redirect to a result page so a browser refresh doesn't resubmit the form.
# app.py — redirect to a named route
from flask import Flask, url_for, redirect
app = Flask(__name__)
@app.route("/user/<username>")
def profile(username):
return f"Profile of {username}"
@app.route("/old-profile")
def old_profile():
# Send visitors of the old URL to the new one
return redirect(url_for("profile", username="ada"))
# Inspect the redirect WITHOUT a browser:
with app.test_client() as client:
r = client.get("/old-profile")
print(r.status_code) # 302
print(r.headers["Location"]) # /user/ada
# follow_redirects lands on the final page:
final = client.get("/old-profile", follow_redirects=True)
print(final.data.decode()) # Profile of ada
# ✅ Expected output:
# 302
# /user/ada
# Profile of adaThe status code 302 plus the Location: /user/ada header is exactly what a real browser receives — then it automatically requests that new URL.
🔬 url_for Cheat Sheet
| You want... | Code |
|---|---|
| A simple route's URL | url_for("home") |
| A URL with a parameter | url_for("profile", username="ada") |
| A query string | url_for("search", q="x") |
| A static asset | url_for("static", filename="a.css") |
| To redirect | redirect(url_for("home")) |
🎯 Your Turn: Fill in the Blanks
Complete the code so it builds a profile URL, a search URL with a query, and a redirect. Replace each ___.
# 🎯 YOUR TURN: replace each ___ to finish the routing
from flask import Flask, url_for, redirect
app = Flask(__name__)
@app.route("/user/<username>")
def profile(username):
return f"Profile: {username}"
@app.route("/search")
def search():
return "results"
with app.test_request_context():
# Build /user/max
print(url_for("___", username="max")) # endpoint name
# Build /search?q=python
print(url_for("search", ___="python")) # query param name
# A redirect to the profile page:
@app.route("/me")
def me():
return ___(url_for("profile", username="max")) # which function redirects?
with app.test_client() as client:
print(client.get("/me").headers["Location"]) # /user/maxCommon Errors & Quick Reference
❌ BuildError: Could not build url for endpoint
The endpoint name is wrong, or you forgot a required route parameter. Pass the exact view function name and every it declares, e.g. url_for("profile", username="ada").
❌ Working outside of request context
Calling url_for at import time fails because there's no request. Call it inside a view, a template, or a test_request_context() / app_context() block.
| Task | Code |
|---|---|
| URL with param | url_for("v", id=1) |
| Query string | url_for("v", q="x") |
| Static file | url_for("static", filename="x") |
| Redirect | redirect(url_for("v")) |
🏆 Mini Challenge: A Short-Link Redirector
Build a route that redirects a short code to a full article URL.
- Create an article route at /article/<slug>
- Create a go route that redirects to it using url_for
- Confirm the Location header points at the article
# 🏆 Mini Challenge: a short-link redirector
from flask import Flask, url_for, redirect
app = Flask(__name__)
@app.route("/article/<slug>")
def article(slug):
return f"Reading: {slug}"
@app.route("/go")
def go():
# Redirect short link /go to the full article page
return redirect(url_for("article", slug="flask-tips"))
with app.test_client() as client:
r = client.get("/go")
print(r.status_code, r.headers["Location"]) # 302 /article/flask-tips
print(client.get("/go", follow_redirects=True).data.decode()) # Reading: flask-tips❓ Frequently Asked Questions
Lesson complete — your links will never go stale!
You can build URLs from view names, attach query parameters, reference static files, and redirect cleanly with redirect(url_for(...)).
Practice quiz
What does url_for build a URL from?
- A hardcoded path string
- The current request URL
- A regex pattern
- A view's endpoint name
Answer: A view's endpoint name. url_for('profile') looks up the route registered for that view's endpoint and returns its path.
What does url_for('profile', username='ada') produce for /user/<username>?
- /profile?username=ada
- /user/ada
- /user/<username>
- /ada
Answer: /user/ada. The keyword matches the route parameter, filling the dynamic segment to give /user/ada.
How does url_for handle a keyword that is NOT a route parameter?
- It appends it as a query string
- It raises an error
- It ignores it
- It adds it to the path
Answer: It appends it as a query string. Extra keywords become a query string, so url_for('search', q='flask', page=2) gives /search?q=flask&page=2.
Which endpoint name links static files?
- files
- assets
- static
- public
Answer: static. url_for('static', filename='css/style.css') uses the built-in 'static' endpoint to return /static/css/style.css.
Which idiomatic call redirects to a named view?
- go(url_for('home'))
- redirect('/home')
- send(url_for('home'))
- redirect(url_for('home'))
Answer: redirect(url_for('home')). redirect(url_for('view')) builds the destination from a name rather than a hardcoded path.
What HTTP status code does redirect() send by default?
- 200
- 302
- 404
- 500
Answer: 302. redirect() returns a 302 response with a Location header pointing at the target URL.
Why avoid hardcoding URLs like '/user/ada'?
- They break when you rename a route or add a prefix
- They load more slowly
- They are not valid HTML
- They disable caching
Answer: They break when you rename a route or add a prefix. Hardcoded strings break when the URL map changes; url_for asks Flask for the current path.
Why must url_for run inside a request or test_request_context()?
- To compress the URL
- Because it needs an active request context
- To enable CSRF
- To cache the result
Answer: Because it needs an active request context. Calling url_for at import time fails; it needs a view, a template, or a test_request_context() block.
Which header carries the destination of a 302 redirect?
- Refresh
- Redirect
- Location
- Forward
Answer: Location. A redirect sets the Location header, which the browser then follows to the new URL.
What raises a BuildError from url_for?
- Passing too many query params
- Using it inside a template
- Calling redirect first
- A wrong endpoint name or a missing route parameter
Answer: A wrong endpoint name or a missing route parameter. BuildError means the endpoint name is wrong or a required <param> was not supplied.
Continue this course
- Previous: Jinja2 Deep Dive: Filters, Macros & Inheritance
- Next: Working with Cookies — Set, read, and delete cookies with the right security flags
- Quick reference: Flask cheat sheet › Setup & Routes
Frequently asked questions
What does url_for do in Flask?
url_for builds a URL for a view by its function name instead of you typing the path. url_for('profile', username='ada') looks up the route registered for the profile view and produces /user/ada. If you ever change the route's path, every url_for call updates automatically.
Why shouldn't I hardcode URLs in Flask?
Hardcoded strings like '/user/ada' break the moment you rename a route, mount the app under a prefix, or add a parameter. url_for asks Flask for the current path, so links and redirects stay correct no matter how the URL map changes.
How do I add query parameters with url_for?
Any keyword argument that is NOT a route parameter becomes a query string. url_for('search', q='flask', page=2) produces /search?q=flask&page=2 because q and page aren't part of the route pattern, so Flask appends them after the ?.
How do I link to static files?
Use url_for('static', filename='css/style.css'), which returns /static/css/style.css. The 'static' endpoint is built in, so this is the recommended way to reference CSS, JavaScript, and images instead of hardcoding /static/ paths.