Skillversitas

Static Files

Static files - images, CSS, JavaScript, downloads - are served directly from disk without touching your endpoint code. FastAPI exposes the Starlette StaticFiles handler via app.mount, which maps a URL prefix to a directory and serves files with correct content types and conditional requests.

Mounting a Directory

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")

@app.get("/")
def root():
    return {"docs": "/docs", "logo": "/static/logo.png"}

After this, /static/logo.png serves static/logo.png, /static/css/app.css serves that file, and the directory listing behavior is disabled by default.

Useful Options

app.mount(
    "/files",
    StaticFiles(directory="downloads", check_dir=False),
    name="files",
)

# serve with directory listing enabled:
app.mount(
    "/media",
    StaticFiles(directory="media", html=True),
    name="media",
)
  • check_dir=False - do not fail startup when the directory is missing yet.
  • html=True - serve index.html and directory pages; useful for small SPA deployments.
  • package_dir - serve files bundled inside a Python package.

Forcing a Download

StaticFiles serves inline; to trigger a browser download, use FileResponse with a filename:

from fastapi.responses import FileResponse

@app.get("/report.pdf")
def report():
    return FileResponse("reports/report.pdf", filename="report.pdf")

SPAs and Reverse Proxies

For single-page apps, the cleanest setup is to serve the built bundle (index.html plus assets) from StaticFiles with html=True, and let a reverse proxy handle client-side routing fallbacks. In production, many teams serve static files entirely from the proxy or CDN instead.

Summary

Serve static content with app.mount("/static", StaticFiles(directory="static")) so file requests bypass your route handlers, use html=True for index-based serving, and use FileResponse when you need to force a download with a custom filename.