# How to Configure CORS Origins for Production Deployments in Open Notebook

> Secure your Open Notebook production deployment by configuring CORS origins. Easily restrict API access by setting the CORS_ORIGINS environment variable to your trusted frontend URLs.

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

---

**Set the `CORS_ORIGINS` environment variable to a comma-separated list of your trusted frontend URLs (e.g., `https://app.example.com`) before starting the API to restrict cross-origin requests to specific domains only.**

Open Notebook uses FastAPI's `CORSMiddleware` to manage cross-origin resource sharing between the API and frontend clients. By default, the application accepts requests from any origin (`"*"`), which is convenient for local development but insecure for production environments. This guide explains how to properly **configure CORS origins for production deployments** using the environment variable parsing logic implemented in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py).

## Understanding the Default CORS Behavior

When the Open Notebook API starts without the `CORS_ORIGINS` environment variable set, it falls back to a permissive wildcard configuration. In [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 63-67), the code checks for the presence of `_cors_origins_raw` and defaults to `["*"]` if the variable is missing.

The application sets `CORS_IS_DEFAULT_WILDCARD = True` when this fallback occurs and logs a security warning to indicate that all origins are currently allowed. This default behavior is intentionally insecure for development convenience but must be overridden before deploying to production.

## Configuring Allowed Origins via Environment Variables

To restrict the API to specific domains, set the `CORS_ORIGINS` environment variable to a comma-separated list of exact origin URLs. The API parses this value during module initialization and passes the resulting list to FastAPI's middleware.

### Parsing Logic in api/main.py

The `_parse_cors_origins` function (lines 53-58) handles the transformation of the environment string into a validated list:

```python

# 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

```

This function strips whitespace from the input, handles the special `"*"` case, and splits comma-separated values into a clean list of origin strings.

### Middleware Registration

After parsing, the origins are passed to `CORSMiddleware` (lines 88-95):

```python

# api/main.py – attaching the middleware

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

```

The configuration also applies to custom exception handlers through the `_cors_headers` implementation (lines 67-88), ensuring error responses include the appropriate `Access-Control-Allow-Origin` header.

## Production Deployment Configuration

For production deployments, create a `.env` file at the repository root or inject the variables through your container orchestration platform.

### Example Production Environment File

```dotenv

# 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

```

**Critical:** Changes to `CORS_ORIGINS` require a full restart of the API process because the variable is parsed only once at module load time. Hot-reloading does not refresh this configuration.

## Verifying Your CORS Configuration

After deployment, confirm your CORS restrictions are active:

1. **Check startup logs** for the message "CORS allowed origins:" followed by your specific domain list (not `["*"]`).
2. **Inspect response headers** from a frontend request to verify `Access-Control-Allow-Origin` matches your configured origin exactly.
3. **Test rejection** by attempting a request from an unauthorized domain, which should fail the CORS preflight or actual request.

## Summary

- **Default risk**: Without `CORS_ORIGINS`, the API allows all origins (`["*"]`) and logs a security warning.
- **Configuration method**: Set `CORS_ORIGINS` to a comma-separated list of trusted frontend URLs with protocols.
- **Implementation location**: The parsing logic resides in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 53-58) with middleware registration at lines 88-95.
- **Restart required**: Environment changes only take effect after restarting the API process.
- **Error handling**: CORS headers are included in error responses through custom exception handlers.

## Frequently Asked Questions

### What happens if I don't set CORS_ORIGINS in production?

If you omit the `CORS_ORIGINS` variable, the API defaults to allowing all origins (`["*"]`). While this won't break functionality, it exposes your API to cross-origin requests from any website, potentially allowing malicious sites to make authenticated requests. The application logs a warning at startup to alert you of this insecure configuration.

### Can I use wildcards or patterns in CORS_ORIGINS?

No, the `_parse_cors_origins` function in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) does not support pattern matching or subdomain wildcards (except the single `"*"` value to allow all origins). You must specify each exact origin URL, including the protocol (e.g., `https://app.example.com`). For multiple origins, separate them with commas without spaces.

### Why do I need to restart the API after changing CORS_ORIGINS?

The environment variable is read and parsed only once when the [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) module loads during startup. The resulting list is stored in the `CORS_ALLOWED_ORIGINS` constant and passed to the middleware. Changes to environment variables are not hot-reloaded, so you must restart the process to re-parse the configuration.

### Do CORS headers apply to error responses in Open Notebook?

Yes, the Open Notebook API ensures CORS headers are present even on error responses. The custom exception handlers in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 67-88) include the `_cors_headers` logic, guaranteeing that browser clients receive the `Access-Control-Allow-Origin` header even when the API returns 4xx or 5xx status codes.