Open Notebook CORS Configuration Process: `_parse_cors_origins` and Middleware Setup

Open Notebook configures CORS by reading the CORS_ORIGINS environment variable, parsing it with the _parse_cors_origins helper in api/main.py, and registering CORSMiddleware in the FastAPI app so only trusted origins can access the API.

The lfnovo/open-notebook repository relies on FastAPI to serve its API endpoints, and cross-origin access is controlled through a dedicated configuration flow defined in api/main.py. Understanding the Open Notebook CORS configuration process—including the _parse_cors_origins function and CORS middleware setup—ensures you can lock down production deployments while keeping local development frictionless.

How CORS Origins Are Parsed in api/main.py

All CORS logic is centralized in the FastAPI entry point at api/main.py. The configuration begins by reading a single environment variable and transforming it into a validated list of allowed origins.

The _parse_cors_origins Function

At lines 53–58, the private helper _parse_cors_origins normalizes the raw environment string:

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()]

This function performs three tasks critical to the Open Notebook CORS configuration:

  • Strips whitespace from the overall input.
  • Returns a wildcard list ["*"] if the value is exactly *.
  • Splits comma-separated values and filters out empty entries.

Environment Variable Handling

Immediately after the helper, lines 62–64 load and expose the runtime constants used across the module:

_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

Here is how these statements behave at import time:

  1. The raw CORS_ORIGINS environment variable is read once.
  2. If the variable is missing, the fallback "*" is passed to _parse_cors_origins, which returns ["*"].
  3. CORS_IS_DEFAULT_WILDCARD is set to True when the variable is absent, letting downstream code detect when the wildcard default is active.

When CORS_ORIGINS is omitted, Open Notebook logs a warning and defaults to allowing any origin.

Building CORS Headers for Error Responses

Because exception handlers run before middleware can attach headers, successful responses automatically receive CORS headers from CORSMiddleware, but error responses do not. Open Notebook solves this with a manual header builder.

The _cors_headers Helper

Lines 67–86 define _cors_headers, which replicates Starlette’s CORS behavior for custom error responses:

def _cors_headers(request: Request) -> dict[str, str]:
    """Build CORS headers for error responses."""
    origin = request.headers.get("origin")
    headers = {
        "Access-Control-Allow-Credentials": "true",
        "Access-Control-Allow-Methods": "*",
        "Access-Control-Allow-Headers": "*",
    }
    if origin and ("*" in CORS_ALLOWED_ORIGINS or origin in CORS_ALLOWED_ORIGINS):
        headers["Access-Control-Allow-Origin"] = origin
        headers["Vary"] = "Origin"
    return headers

This helper inspects the incoming request’s Origin header. If the origin is permitted, it injects Access-Control-Allow-Origin and Vary: Origin, ensuring the browser does not block error payloads on cross-origin calls.

Registering the CORS Middleware in FastAPI

With the parsed origins and header helper in place, the final step is wiring the standard CORSMiddleware into the application stack.

Middleware Order and Registration

At lines 88–95, the middleware is added after the custom authentication middleware:


# Add CORS middleware last (so it processes first)

app.add_middleware(
    CORSMiddleware,
    allow_origins=CORS_ALLOWED_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

In FastAPI, the last middleware registered runs first for each incoming request. By adding CORSMiddleware after api/auth.py’s authentication layer, Open Notebook ensures that CORS pre-flight OPTIONS requests are handled before any auth checks run. The allow_origins parameter receives the list produced by _parse_cors_origins, keeping the policy synchronized with the environment configuration.

Production and Development Configuration

You control the entire CORS policy through the CORS_ORIGINS environment variable.

For local development, a single origin is sufficient:


# .env

CORS_ORIGINS=http://localhost:3000
OPEN_NOTEBOOK_ENCRYPTION_KEY=supersecret

For production, explicitly enumerate trusted front-end URLs as a comma-separated list:

CORS_ORIGINS=https://app.example.com,https://admin.example.com

After updating the environment, restart the API server:

uvicorn api.main:app --host 0.0.0.0 --port 5055

A typical front-end client—such as a React application using Axios—must send credentials to match the allow_credentials=True setting:

// frontend/src/api/client.ts
import axios from "axios";

export const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  withCredentials: true, // required for CORS credentials
});

When an error such as 413 Payload Too Large occurs, Open Notebook’s exception handlers can merge the manual CORS headers so the browser still receives the response:

@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request: Request, exc: StarletteHTTPException):
    return JSONResponse(
        status_code=exc.status_code,
        content={"detail": exc.detail},
        headers={**(exc.headers or {}), **_cors_headers(request)},
    )

Summary

  • _parse_cors_origins in api/main.py converts the CORS_ORIGINS string into a cleaned list or a wildcard.
  • CORS_ALLOWED_ORIGINS and CORS_IS_DEFAULT_WILDCARD are computed once at import time.
  • CORSMiddleware is registered last in api/main.py so it executes first on every request, including pre-flight checks.
  • _cors_headers manually adds CORS fields to error responses that bypass the middleware.
  • Setting CORS_ORIGINS explicitly in production disables the default wildcard and restricts access to trusted domains.

Frequently Asked Questions

What does the _parse_cors_origins function do in Open Notebook?

The _parse_cors_origins function located in api/main.py takes the raw CORS_ORIGINS environment variable, trims whitespace, and returns ["*"] for a wildcard or a list of comma-separated origins. It ensures the value is clean before being passed to FastAPI’s CORSMiddleware.

Why does Open Notebook add CORS middleware after authentication?

Open Notebook registers CORSMiddleware after the custom authentication middleware in api/auth.py because FastAPI executes middleware in reverse registration order. Adding CORS last places it at the outermost layer, so it intercepts pre-flight OPTIONS requests before authentication logic runs.

How does Open Notebook handle CORS headers on error responses?

Because CORSMiddleware does not run for responses raised inside exception handlers, Open Notebook defines _cors_headers in api/main.py. This helper computes Access-Control-Allow-Origin and other headers based on the permitted origins list, and custom exception handlers merge them into the response.

What happens if CORS_ORIGINS is not set?

If the CORS_ORIGINS variable is absent, Open Notebook falls back to "*", sets CORS_IS_DEFAULT_WILDCARD to True, logs a warning, and permits cross-origin requests from any domain. This default is convenient for local development but should be overridden in production.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →