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

> Learn the Open Notebook CORS configuration process. Discover how _parse_cors_origins and FastAPI middleware setup restrict API access to trusted origins for secure development.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-06

---

**Open Notebook configures CORS by reading the `CORS_ORIGINS` environment variable, parsing it with the `_parse_cors_origins` helper in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)

All CORS logic is centralized in the FastAPI entry point at [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/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:

```python
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:

```python
_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:

```python
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:

```python

# 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`](https://github.com/lfnovo/open-notebook/blob/main/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:

```dotenv

# .env

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

```

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

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

```

After updating the environment, restart the API server:

```bash
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:

```tsx
// 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:

```python
@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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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.