Linking Static Files (CSS/JS)
Static files are assets like:
- CSS
- JavaScript
- images
Where static files live
Section titled “Where static files live”By default:
static/folder next to your app/package
Example:
myapp/
app.py
static/
styles.css
templates/
base.htmlLink CSS using url_for
Section titled “Link CSS using url_for”In base.html:
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">Link JavaScript
Section titled “Link JavaScript”<script src="{{ url_for('static', filename='app.js') }}"></script>Why url_for matters here too
Section titled “Why url_for matters here too”- handles URL prefixes (reverse proxy setups)
- avoids hardcoded
/static/... - works cleanly with Blueprints and app factories
Production note
Section titled “Production note”In production, Nginx or a CDN often serves static files more efficiently.
But the URL patterns should still work the same.
Always build static URLs with url_for
Section titled “Always build static URLs with url_for”A hard-coded /static/style.css breaks the moment the app is mounted under a prefix, or
static_url_path changes, or you move to a CDN. url_for derives the path from the
app’s configuration:
flowchart TD
U["url_for('static', filename='css/site.css')"] --> S["static_url_path
default /static"]
S --> P["/static/css/site.css"]
U --> E["_external=True"] --> A["http://localhost/static/..."]
U --> V["extra kwargs become the query string"] --> C["/static/style.css?v=7"]
Measured:
| call | result |
|---|---|
url_for('static', filename='style.css') | /static/style.css |
url_for('static', filename='css/site.css') | /static/css/site.css |
url_for('static', filename='style.css', v=7) | /static/style.css?v=7 |
url_for('static', filename='s.css', _external=True) | http://localhost/static/s.css |
<link rel="stylesheet" href="{{ url_for('static', filename='css/site.css') }}">
<script src="{{ url_for('static', filename='js/app.js') }}" defer></script>
<img src="{{ url_for('static', filename='img/logo.svg') }}" alt="Logo">Note that the static endpoint exists without you registering it — creating the Flask
object adds the rule /static/<path:filename> to url_map.
Cache busting is what the extra argument is for
Section titled “Cache busting is what the extra argument is for”Browsers cache static files aggressively, which is what you want until you ship a change and users keep the old CSS. Any extra keyword becomes a query parameter:
url_for("static", filename="style.css", v=7)
# /static/style.css?v=7Change the value and the URL changes, so the browser treats it as a new resource. Rather than editing a number by hand, derive it from something that changes with the file:
import os
@app.context_processor
def static_version():
def versioned(filename):
path = os.path.join(app.static_folder, filename)
stamp = int(os.path.getmtime(path))
return url_for("static", filename=filename, v=stamp)
return {"versioned": versioned}<link rel="stylesheet" href="{{ versioned('css/site.css') }}">Now the URL changes exactly when the file does, and never otherwise.
See it move
Section titled “See it move”Check yourself
Section titled “Check yourself”-
Why prefer url_for('static', filename='css/site.css') to a literal /static/css/site.css?
A hard-coded path is right only for one deployment. Mount the app under /app or change static_url_path and it 404s — after working perfectly in development.
pch.quizShowAnswer
B — it derives the path from static_url_path and any mount prefix, so it stays correct when the app is deployed under a sub-path — A hard-coded path is right only for one deployment. Mount the app under /app or change static_url_path and it 404s — after working perfectly in development.
-
What does url_for('static', filename='style.css', v=7) produce?
Unrecognised keywords become query parameters. Changing the value changes the URL, so the browser fetches a fresh copy — the standard cache-busting trick.
pch.quizShowAnswer
B — /static/style.css?v=7 — Unrecognised keywords become query parameters. Changing the value changes the URL, so the browser fetches a fresh copy — the standard cache-busting trick.
-
Which route exists in url_map without you writing it?
Creating the Flask object registers the static endpoint, which is why url_for('static', ...) works immediately.
pch.quizShowAnswer
B — /static/<path:filename> — Creating the Flask object registers the static endpoint, which is why url_for('static', ...) works immediately.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading