Quick start¶
Create the app with its built-in docs disabled, send your Content-Security-Policy, and call setup():
import fastapi_csp_docs
CSP = (
"default-src 'self'; "
"script-src 'self' https://cdn.jsdelivr.net; "
"style-src 'self' https://cdn.jsdelivr.net https://fonts.googleapis.com "
"'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' "
"'sha256-QMIg+bpjm3JdElJ388KYke01izlUW0UoNOeKjpMxdgc='; "
"font-src 'self' https://fonts.gstatic.com; "
"img-src 'self' data: https://fastapi.tiangolo.com https://cdn.redoc.ly; "
"connect-src 'self' https://cdn.jsdelivr.net; "
"worker-src 'self' blob:"
)
app = FastAPI(docs_url=None, redoc_url=None)
@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)
Start the server and open /docs and /redoc: both pages render, and the browser console reports no CSP violations.
setup() registers these routes, all excluded from the OpenAPI schema:
| Route | Serves |
|---|---|
/docs |
Swagger UI page |
/docs/swagger-initializer.js |
Script that starts Swagger UI |
/docs/oauth2-redirect |
OAuth2 redirect page |
/docs/oauth2-redirect.js |
OAuth2 redirect script |
/redoc |
ReDoc page |
/redoc/redoc.css |
CSS reset ReDoc needs |
Built-in docs still enabled¶
setup() raises RuntimeError when the page it would register is still enabled on the app. With FastAPI() and no arguments, the message is:
fastapi_csp_docs.setup() cannot mount Swagger UI at '/docs' because the app still serves its built-in docs at '/docs'. Create the FastAPI app with docs_url=None to disable it first, e.g. FastAPI(docs_url=None).
Next steps¶
- Writing the CSP explains every directive in this policy and how to narrow it.
- Fully self-hosted docs removes the CDN.