# CORS Middleware Configuration and Security in Open Notebook: A Complete Guide

> Secure your Open Notebook API with CORS middleware. Learn how to configure and restrict cross-origin access for production environments in this complete guide.

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

---

**Open Notebook implements CORS through environment variable parsing in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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):

```python
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 is `True` when `CORS_ORIGINS` was 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:

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

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

```python
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=True` and 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:

1. **Set explicit origins** in your environment configuration (Docker Compose, Kubernetes ConfigMap, or `.env`):

   ```dotenv
   # .env (production)

   CORS_ORIGINS=https://notebook.example.com,https://admin.notebook.example.com
   ```

2. **Validate HTTPS schemes** in your origin list to ensure encrypted cross-origin traffic.

3. **Access configuration programmatically** to verify settings at runtime:

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

4. **Combine with reverse proxy CORS** (NGINX or Traefik) to enforce headers on responses that bypass FastAPI's exception handlers.

5. **Review OPTIONS handling** in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/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_ORIGINS` environment variable parsed in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) using the `_parse_cors_origins` helper.
- The middleware is registered at lines 88-95 as the outermost layer, with `allow_credentials=True` requiring 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_ORIGINS` constant 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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/security.md).