python
44 lines · 8 steps
Serving cacheable static assets in Flask
A Flask blueprint that streams files safely while using ETags and cache headers to skip redundant transfers.
Explained by
highlit
1import hashlib
2import os
3from datetime import timedelta
4
5from flask import Blueprint, abort, current_app, request, send_file
6from werkzeug.utils import safe_join
7
8assets = Blueprint("assets", __name__)
9
10
11def _compute_etag(path, stat):
12 digest = hashlib.blake2b(digest_size=16)
13 digest.update(str(stat.st_mtime_ns).encode())
14 digest.update(str(stat.st_size).encode())
15 digest.update(os.fsencode(path))
16 return f'"{digest.hexdigest()}"'
17
18
19@assets.route("/assets/<path:filename>")
20def serve_asset(filename):
21 root = current_app.config["ASSET_ROOT"]
22 full_path = safe_join(root, filename)
23 if full_path is None or not os.path.isfile(full_path):
24 abort(404)
25
26 stat = os.stat(full_path)
27 etag = _compute_etag(full_path, stat)
28
29 if etag in request.if_none_match:
30 response = current_app.response_class(status=304)
31 response.set_etag(etag.strip('"'))
32 return response
33
34 response = send_file(
35 full_path,
36 conditional=True,
37 last_modified=stat.st_mtime,
38 max_age=timedelta(days=30),
39 )
40 response.set_etag(etag.strip('"'))
41 response.cache_control.public = True
42 response.cache_control.immutable = True
43 response.cache_control.max_age = int(timedelta(days=30).total_seconds())
44 return response
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1An ETag derived from file metadata lets clients revalidate and receive a cheap 304 instead of the full body.
- 2safe_join prevents path traversal by rejecting filenames that escape the configured root directory.
- 3Setting cache-control to public and immutable tells browsers they can reuse the asset without ever revalidating.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/serving-cacheable-static-assets-in-flask-explained-python-af45/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.