Fully self-hosted docs¶
Serve the vendor files from your app and pass their URLs to setup(). script-src then falls back to default-src 'self', with no external origin:
from fastapi.staticfiles import StaticFiles
import fastapi_csp_docs
CSP = (
"default-src 'self'; "
"style-src 'self' "
"'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' "
"'sha256-QMIg+bpjm3JdElJ388KYke01izlUW0UoNOeKjpMxdgc='; "
"img-src 'self' data: https://cdn.redoc.ly; "
"worker-src 'self' blob:"
)
app = FastAPI(docs_url=None, redoc_url=None)
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
@app.middleware("http")
async def add_csp_header(request: Request, call_next) -> Response:
response = await call_next(request)
response.headers["Content-Security-Policy"] = CSP
return response
fastapi_csp_docs.setup(
app,
swagger_js_url="/static/swagger-ui-bundle.js",
swagger_css_url="/static/swagger-ui.css",
swagger_favicon_url="/static/favicon.png",
redoc_js_url="/static/redoc.standalone.js",
redoc_favicon_url="/static/favicon.png",
google_fonts_css_url="/static/fonts.css",
)
STATIC_DIR holds these files. examples/static/ has a copy of each, at the versions setup() loads by default:
| File | setup() parameter |
|---|---|
swagger-ui-bundle.js |
swagger_js_url |
swagger-ui.css |
swagger_css_url |
redoc.standalone.js |
redoc_js_url |
favicon.png |
swagger_favicon_url, redoc_favicon_url |
fonts.css and fonts/ |
google_fonts_css_url |
The only external origin left in the policy is https://cdn.redoc.ly in img-src, for ReDoc's logo. The two hashes and worker-src 'self' blob: serve ReDoc's own runtime, wherever its bundle comes from.
URLs under a prefix¶
setup() puts the request's ASGI root_path in front of an asset URL that starts with a single /. With the app mounted under /api, /static/swagger-ui-bundle.js becomes /api/static/swagger-ui-bundle.js. FastAPI's own docs functions use asset URLs as given.
Absolute (https://...), protocol-relative (//...) and relative URLs are used as given.
The full app is examples/self_hosted_app.py.