CORS Middleware Configuration and Security in Open Notebook: A Complete Guide
Open Notebook implements CORS through environment variable parsing in api/main.py, defaulting to wildcard origins for development while providing explicit mechanisms to restrict cross-origin access in production.
Open Notebook is an open-source knowledge management platform built on FastAPI that relies on Starlette's CORSMiddleware to manage cross-origin requests. Understanding the CORS middleware configuration and security implementation is essential for protecting your API against unauthorized cross-origin access while maintaining developer productivity. This guide examines the source code in lfnovo/open-notebook to explain how origins are parsed, enforced, and applied to both successful responses and error conditions.
How CORS Origins Are Parsed and Stored
The configuration system reads the CORS_ORIGINS environment variable at import time and transforms it into structured constants used by the middleware stack.
The _parse_cors_origins Helper
In api/main.py, the helper function _parse_cors_origins processes the raw environment string into a list of allowed origins. This function supports either a single wildcard ("*") or a comma-separated list of URLs (lines 53-59):
def _parse_cors_origins(cors_origins_env: Optional[str]) -> List[str]:
if not cors_origins_env:
return ["*"]
return [origin.strip() for origin in cors_origins_env.split(",")]
This parsing creates two module-level constants:
CORS_ALLOWED_ORIGINS: The final list of origins passed to the middleware. Defaults to["*"]when the environment variable is missing.CORS_IS_DEFAULT_WILDCARD: A boolean flag that isTruewhenCORS_ORIGINSwas not set, signaling the "allow-everything" fallback state.
Registering CORSMiddleware in FastAPI
The middleware is added last in the FastAPI application stack (lines 88-95), which makes it the outermost layer in Starlette's processing order. This ensures CORS headers are evaluated before other middlewares process the request:
app.add_middleware(
CORSMiddleware,
allow_origins=CORS_ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Placing the middleware at the end of the stack guarantees that preflight OPTIONS requests receive CORS headers immediately, allowing the actual request to proceed through subsequent authentication and validation layers.
Default Wildcard Security Warnings
When CORS_ORIGINS is not configured, Open Notebook emits a startup warning to alert operators that the API is currently permissive (lines 63-69):
if CORS_IS_DEFAULT_WILDCARD:
logger.warning(
"CORS_ORIGINS is not set — API accepts cross-origin requests from any origin (default: '*')"
)
This logging serves as a critical security reminder that the deployment is using insecure defaults suitable only for local development. The warning appears before the server accepts traffic, ensuring visibility during container orchestration startup.
Ensuring CORS Headers on Error Responses
Browser security requires CORS headers on all responses, including 4xx and 5xx errors. Open Notebook defines a private helper _cors_headers(request) (lines 67-88) that constructs appropriate headers based on the request's Origin header.
Every custom exception handler merges these headers into JSONResponse objects. For example, the HTTP exception handler (lines 98-126) and the "Payload Too Large" handler (lines 172-215) both use:
from api.main import _cors_headers
@app.exception_handler(HTTPException)
async def custom_http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail},
headers=_cors_headers(request),
)
This pattern prevents browsers from blocking error details while ensuring malicious sites cannot exploit error responses to probe the API.
Security Implications and Credentials Handling
The interaction between allow_credentials=True and wildcard origins requires careful handling. When CORS_ALLOWED_ORIGINS is ["*"], browsers reject credentialed requests for security reasons. The _cors_headers helper reflects the origin only when it matches an explicit entry in the allowed list (lines 84-86), ensuring credentials are never sent to unauthorized domains.
Key security considerations include:
- Wildcard (
"*"): Allows any origin to read responses but prevents credential sharing. Safe for development, insecure for production. - Explicit Origins: Restricts access to trusted front-end URLs. Required when
allow_credentials=Trueand cookies/auth headers are used. - HTTPS Enforcement: Production origins should always use HTTPS to prevent man-in-the-middle attacks on cross-origin traffic.
Production Security Hardening Guide
To secure your Open Notebook deployment, implement the following hardening steps:
-
Set explicit origins in your environment configuration (Docker Compose, Kubernetes ConfigMap, or
.env):# .env (production) CORS_ORIGINS=https://notebook.example.com,https://admin.notebook.example.com -
Validate HTTPS schemes in your origin list to ensure encrypted cross-origin traffic.
-
Access configuration programmatically to verify settings at runtime:
from api.main import CORS_ALLOWED_ORIGINS, CORS_IS_DEFAULT_WILDCARD print("Allowed origins:", CORS_ALLOWED_ORIGINS) print("Is default wildcard:", CORS_IS_DEFAULT_WILDCARD) -
Combine with reverse proxy CORS (NGINX or Traefik) to enforce headers on responses that bypass FastAPI's exception handlers.
-
Review OPTIONS handling in
api/auth.py, which exempts preflight requests from authentication to ensure CORS negotiation succeeds before credentials are checked.
Summary
- CORS configuration in Open Notebook is driven by the
CORS_ORIGINSenvironment variable parsed inapi/main.pyusing the_parse_cors_originshelper. - The middleware is registered at lines 88-95 as the outermost layer, with
allow_credentials=Truerequiring explicit origin lists for production. - Default wildcard mode triggers a startup warning (lines 63-69) to alert operators of insecure configurations.
- Custom exception handlers inject CORS headers via
_cors_headers()to ensure error responses are accessible to legitimate cross-origin callers. - Production deployments must specify explicit HTTPS origins and audit the
CORS_ALLOWED_ORIGINSconstant to prevent unauthorized credential exposure.
Frequently Asked Questions
What is the default CORS behavior in Open Notebook?
By default, Open Notebook allows cross-origin requests from any origin ("*") if the CORS_ORIGINS environment variable is not set. This default is logged as a warning at startup (lines 63-69 in api/main.py) and is intended only for local development.
How do I configure specific allowed origins for production?
Set the CORS_ORIGINS environment variable to a comma-separated list of URLs: https://app.example.com,https://admin.example.com. The _parse_cors_origins function splits this string into the CORS_ALLOWED_ORIGINS list passed to the middleware, replacing the wildcard with your specific domains.
Why are CORS headers important for error responses?
Browsers apply same-origin policy restrictions to error responses (4xx/5xx status codes). Without CORS headers, legitimate front-end applications cannot read error details from the API. Open Notebook solves this by injecting headers via _cors_headers() in all exception handlers (lines 98-126 and 172-215), ensuring diagnostic information reaches authorized callers without exposing sensitive data to malicious sites.
Can I disable CORS entirely in Open Notebook?
No, the middleware is always active to handle preflight OPTIONS requests required by modern browsers. However, you can effectively restrict access to same-origin only by setting CORS_ORIGINS to your API's own URL, or by implementing additional restrictions at your reverse proxy layer (NGINX/Traefik) as documented in docs/5-CONFIGURATION/security.md.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →