How to Configure CORS Origins for Production Deployments in Open Notebook
Set the CORS_ORIGINS environment variable to a comma-separated list of your frontend URLs (e.g., https://notebook.example.com) before deploying to production, or the API will default to allowing all origins (*), creating a security vulnerability.
The open-notebook API uses FastAPI's CORSMiddleware to manage cross-origin resource sharing, controlling which frontend domains can access your backend. By default, the application runs with permissive settings suitable for local development, accepting requests from any origin. Before deploying to production, you must configure CORS origins using the CORS_ORIGINS environment variable to restrict access to only your trusted frontend applications.
Understanding the Default CORS Behavior in api/main.py
When the API starts without the CORS_ORIGINS variable set, it falls back to a wildcard configuration that allows any domain to make requests. As implemented in lfnovo/open-notebook, the startup logic in api/main.py (lines 63-67) detects this unset state and logs a security warning.
The default behavior works as follows:
- If
CORS_ORIGINSis missing,CORS_ALLOWED_ORIGINSdefaults to["*"] - The variable
CORS_IS_DEFAULT_WILDCARDis set toTrue, triggering a warning log - All incoming requests receive
Access-Control-Allow-Origin: *headers
This configuration is convenient for development but exposes your API to cross-site request forgery risks in production environments.
How CORS_ORIGINS Parsing Works
The parsing logic resides in the _parse_cors_origins function within api/main.py (lines 53-58). This function transforms the environment variable string into a list of allowed origins that FastAPI's middleware consumes.
# api/main.py – parsing the env var
def _parse_cors_origins(raw: str) -> list[str]:
"""Parse CORS_ORIGINS env value into a list of origins."""
value = raw.strip()
if value == "*":
return ["*"]
return [origin.strip() for origin in value.split(",") if origin.strip()]
_cors_origins_raw = os.getenv("CORS_ORIGINS")
CORS_ALLOWED_ORIGINS = _parse_cors_origins(_cors_origins_raw or "*")
CORS_IS_DEFAULT_WILDCARD = _cors_origins_raw is None
The function handles both wildcard strings and comma-separated domain lists, stripping whitespace from each entry. If you specify a single asterisk, it returns ["*"]; otherwise, it splits on commas to support multiple domains.
Registering the CORS Middleware
After parsing, the application attaches CORSMiddleware to the FastAPI app instance (lines 88-95 in api/main.py):
# api/main.py – attaching the middleware
app.add_middleware(
CORSMiddleware,
allow_origins=CORS_ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
The allow_origins parameter receives your configured list, ensuring the middleware injects the appropriate Access-Control-Allow-Origin headers into every response. The code also applies these headers to custom exception handlers (see _cors_headers at lines 67-88) to ensure CORS compliance even during error responses.
Production Configuration Steps
To properly configure CORS origins for production deployments, follow these four steps:
-
Set
CORS_ORIGINSto your frontend URLs
Specify a comma-separated list of exact origins without trailing slashes:https://notebook.example.com,https://admin.example.com. Do not include paths or wildcards unless you intend to allow all origins. -
Restart the API process
The environment variable is parsed only at module load time. Changes require a full restart or redeployment of the container to take effect. -
Verify startup logs
Check that the application logs contain the line "CORS allowed origins:" followed by your specific domains, confirming the configuration was recognized. -
Test browser requests
Make a request from your frontend application and inspect the response headers. You should seeAccess-Control-Allow-Origin: https://your-domain.comrather than*.
Example Production Environment File
Place this configuration at the root of your repository or inject these values through your container orchestration platform:
# Core security
OPEN_NOTEBOOK_ENCRYPTION_KEY=very-strong-secret-key
OPEN_NOTEBOOK_PASSWORD=super-secure-password
# CORS configuration – replace with your actual frontend domains
CORS_ORIGINS=https://notebook.example.com,https://admin.example.com
# API address (used by the Next.js proxy)
API_URL=https://notebook.example.com
According to the documentation in docs/5-CONFIGURATION/environment-reference.md (lines 73-78), this variable accepts a comma-separated list of allowed origins. The security guidelines in docs/7-DEVELOPMENT/security.md (lines 149-157) explicitly recommend restricting this setting before production deployment.
Summary
- Never use the default wildcard (
*) in production. TheCORS_ORIGINSenvironment variable defaults to allowing all origins if unset, which creates security vulnerabilities. - Use comma-separated exact URLs. The parser in
api/main.pysplits on commas and strips whitespace, supporting multiple frontend domains. - Restart required. Changes to
CORS_ORIGINSonly take effect after restarting the API process. - Verify through logs. Check for the "CORS allowed origins:" startup message to confirm your configuration is active.
Frequently Asked Questions
What happens if I don't set CORS_ORIGINS in production?
If you omit this variable, the API defaults to ["*"] and logs a warning during startup. While functionally allowing your frontend to connect, this also permits any malicious website to make cross-origin requests to your API, potentially exposing sensitive data or allowing unauthorized actions if authentication tokens are present.
Can I use wildcards or patterns in the CORS_ORIGINS value?
The _parse_cors_origins function in api/main.py does not support pattern matching or subdomain wildcards. You must specify exact origins such as https://app.example.com. If you need to support multiple subdomains, list each one explicitly separated by commas.
Do I need to restart the API after changing CORS_ORIGINS?
Yes. The environment variable is read and parsed only when the module loads during startup. The CORS_ALLOWED_ORIGINS constant is set at this time and passed to the middleware. To apply new values, you must restart the Python process or redeploy your container.
Where is the CORS middleware registered in the codebase?
The middleware is attached in api/main.py at lines 88-95 using app.add_middleware(CORSMiddleware, ...). This registration occurs after the _parse_cors_origins function processes your environment variable, ensuring the middleware receives the correct list of allowed origins immediately upon application startup.
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 →