# Configuring CORS for Open Notebook: Best Practices and Security Implementation

> Learn to configure CORS for Open Notebook using FastAPI. Secure your application in production with best practices and implement strict origin validation for enhanced security.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: best-practices
- Published: 2026-06-23

---

**Open Notebook configures CORS through FastAPI's `CORSMiddleware` using the `CORS_ORIGINS` environment variable, defaulting to a wildcard (`*`) in development while integrating with password authentication middleware to enforce strict origin validation in production.**

Configuring CORS for Open Notebook is critical for securing the FastAPI backend against cross-origin attacks while enabling legitimate browser-based frontend interactions. The application implements a sophisticated middleware stack in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) that handles origin parsing, credential passthrough, and custom error response headers. Understanding these implementation details ensures safe deployments across development, single-tenant, and multi-tenant environments.

## How CORS Configuration Works

### Parsing Environment Variables

At startup, Open Notebook reads the **`CORS_ORIGINS`** environment variable in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 53-58) using the helper function **`_parse_cors_origins`**. This function transforms comma-separated URLs into a Python list that feeds directly into the middleware configuration, allowing multiple origins for multi-tenant scenarios.

### Middleware Registration

The **`CORSMiddleware`** is registered in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 88-95) with `allow_credentials=True`, `allow_methods=["*"]`, and `allow_headers=["*"]`. Critically, this middleware is added **after** the `PasswordAuthMiddleware` in the code; due to FastAPI's execution order, this means CORS processing runs **first** on every request, ensuring browsers receive proper headers even when subsequent authentication fails.

## Default Wildcard Behavior and Warnings

If **`CORS_ORIGINS`** is unset, the system defaults to a permissive **`*`** wildcard that allows any origin. The application detects this condition in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 63-71) and emits a startup warning to remind operators to restrict origins before deploying to production. This default facilitates local development but exposes the API to cross-origin attacks if left unconfigured.

## Security Architecture and Middleware Ordering

Open Notebook implements defense-in-depth by layering CORS controls with the **`PasswordAuthMiddleware`** (defined in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) and instantiated in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) lines 15-18). This middleware secures all routes except whitelisted endpoints like `/api/auth/status` and `/api/config`.

The middleware stack ordering is architecturally significant: because **CORS middleware runs before authentication**, rejected credentials still return proper `Access-Control-Allow-Origin` headers to the browser. This prevents CORS errors from masking authentication failures, allowing frontend developers to see actual 401/403 responses in developer tools.

## Error Handling and CORS Headers

The application includes a custom **`_cors_headers`** helper function in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 66-84) that injects CORS headers into all exception responses. Exception handlers starting at line 98 utilize this helper to ensure that **4xx client errors** and **5xx server errors** include appropriate `Access-Control-Allow-Origin` headers. This guarantees legitimate browsers can read error payloads from failed requests while maintaining origin restrictions for disallowed domains.

## Recommended Configurations by Deployment Scenario

**Local Development**: Set `CORS_ORIGINS=http://localhost:3000` to restrict access to your local frontend, minimizing the attack surface even during development.

**Production Single-Tenant**: Configure `CORS_ORIGINS=https://notebook.example.com` with your exact production domain. This prevents cross-site request forgery by ensuring only your deployed UI can request resources.

**Multi-Tenant or SaaS**: Explicitly enumerate tenant domains: `CORS_ORIGINS=https://tenant1.example.com,https://tenant2.example.com`. Never use wildcard patterns or dynamic origin matching when credentials are enabled.

**Public APIs Without Authentication**: Only use `CORS_ORIGINS=*` for truly stateless, public endpoints that require no credentials. **Never** combine wildcards with the `Authorization` header or cookie-based authentication, as browsers explicitly reject credential requests when servers return `Access-Control-Allow-Origin: *`.

## Critical Security Considerations

**Credentials Require Specific Origins**: When `allow_credentials=True` (as implemented in the middleware), browsers require the specific requesting origin in the `Access-Control-Allow-Origin` response header. The **`_cors_headers`** function reflects the request origin only if it appears in the allowed list, satisfying browser security requirements while blocking unauthorized origins.

**Encryption Key Validation**: The application validates **`OPEN_NOTEBOOK_ENCRYPTION_KEY`** during the lifespan check in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 104-114). This ensures credential storage encryption is active before accepting authenticated cross-origin requests, preventing plaintext credential exposure.

**CI/CD Enforcement**: Treat the wildcard startup warning as a hard failure in deployment pipelines. Configure infrastructure-as-code checks to verify that `CORS_ORIGINS` is explicitly set and does not contain `*` in production environments.

## Implementation and Verification

Configure your production environment:

```dotenv

# .env

CORS_ORIGINS=https://notebook.mycompany.com
OPEN_NOTEBOOK_ENCRYPTION_KEY=minimum-32-character-secret-key-for-credential-encryption

```

You can verify the configuration using curl to inspect response headers:

```bash
curl -i -H "Origin: https://notebook.mycompany.com" \
     -H "Authorization: Bearer <your-token>" \
     http://localhost:5055/api/config

```

Expected secure response headers:

```

Access-Control-Allow-Origin: https://notebook.mycompany.com
Access-Control-Allow-Credentials: true

```

## Key Source Files

- **[`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)**: FastAPI application factory containing `_parse_cors_origins`, `CORSMiddleware` registration (lines 88-95), `_cors_headers` helper (lines 66-84), and lifespan encryption validation (lines 104-114).
- **[`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py)**: Password authentication middleware that executes after CORS processing on the request path.
- **[`api/routers/config.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/config.py)**: Exposes configuration endpoints and emits CORS wildcard warnings.
- **[`open_notebook/utils/encryption.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/encryption.py)**: Manages the `OPEN_NOTEBOOK_ENCRYPTION_KEY` for secure credential storage.
- **[`open_notebook/config.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/config.py)**: Centralized environment variable handling.

## Summary

- **Configure `CORS_ORIGINS`** explicitly to prevent the default wildcard from exposing administrative endpoints to cross-origin attacks.
- The middleware stack places **CORS before authentication**, ensuring browsers receive headers on rejected requests and can display proper error messages.
- When `allow_credentials=True`, **never use wildcard origins**—browsers reject credential requests when `Access-Control-Allow-Origin: *` is returned.
- Use the **`_cors_headers`** helper in exception handlers to ensure error responses remain readable by legitimate frontends while maintaining security boundaries.

## Frequently Asked Questions

### What happens if I leave CORS_ORIGINS unset in production?

The application defaults to `*` (allowing all origins) and logs a startup warning. While functional for local development, this configuration exposes authenticated endpoints to cross-origin attacks and prevents credential-based authentication from working in browsers due to security restrictions on wildcards with credentials.

### Why does the CORS middleware run before authentication checks?

In [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py), the `CORSMiddleware` is added after `PasswordAuthMiddleware` in code registration (lines 88-95), but FastAPI executes middleware in reverse order of addition. This architectural choice ensures that CORS headers are present in responses to rejected authentication requests, preventing browsers from masking 401/403 errors behind CORS policy failures.

### Can I use wildcard origins with API authentication enabled?

No. Open Notebook sets `allow_credentials=True` in the CORS middleware, which triggers browser security policies requiring specific origins. When the server returns `Access-Control-Allow-Origin: *`, browsers automatically reject requests containing cookies or `Authorization` headers, breaking authentication completely.

### How do I verify my CORS configuration is secure?

Send a curl request with a spoofed origin header: `curl -i -H "Origin: https://evil.com" http://localhost:5055/api/config`. Verify that the response **does not** contain `Access-Control-Allow-Origin: https://evil.com` or wildcards. Then test with your legitimate domain to confirm `Access-Control-Allow-Credentials: true` appears only for allowed origins.