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

> Secure your Open Notebook production deployment by configuring CORS origins. Learn to set the CORS_ORIGINS environment variable to protect your API from unauthorized access. Avoid default security risks.

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

---

**Set the `CORS_ORIGINS` environment variable to a comma-separated list of your frontend URLs (e.g., `https://notebook.example.com`) before deploying to production, or the API will default to allowing all origins (`*`), creating a security vulnerability.**

The open-notebook API uses FastAPI's `CORSMiddleware` to manage cross-origin resource sharing, controlling which frontend domains can access your backend. By default, the application runs with permissive settings suitable for local development, accepting requests from any origin. Before deploying to production, you must **configure CORS origins** using the `CORS_ORIGINS` environment variable to restrict access to only your trusted frontend applications.

## Understanding the Default CORS Behavior in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)

When the API starts without the `CORS_ORIGINS` variable set, it falls back to a wildcard configuration that allows any domain to make requests. As implemented in `lfnovo/open-notebook`, the startup logic in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 63-67) detects this unset state and logs a security warning.

The default behavior works as follows:

- If `CORS_ORIGINS` is missing, `CORS_ALLOWED_ORIGINS` defaults to `["*"]`
- The variable `CORS_IS_DEFAULT_WILDCARD` is set to `True`, triggering a warning log
- All incoming requests receive `Access-Control-Allow-Origin: *` headers

This configuration is convenient for development but exposes your API to cross-site request forgery risks in production environments.

## How `CORS_ORIGINS` Parsing Works

The parsing logic resides in the `_parse_cors_origins` function within [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) (lines 53-58). This function transforms the environment variable string into a list of allowed origins that FastAPI's middleware consumes.

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

```

The function handles both wildcard strings and comma-separated domain lists, stripping whitespace from each entry. If you specify a single asterisk, it returns `["*"]`; otherwise, it splits on commas to support multiple domains.

## Registering the CORS Middleware

After parsing, the application attaches `CORSMiddleware` to the FastAPI app instance (lines 88-95 in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)):

```python

# api/main.py – attaching the middleware

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

```

The `allow_origins` parameter receives your configured list, ensuring the middleware injects the appropriate `Access-Control-Allow-Origin` headers into every response. The code also applies these headers to custom exception handlers (see `_cors_headers` at lines 67-88) to ensure CORS compliance even during error responses.

## Production Configuration Steps

To properly **configure CORS origins for production deployments**, follow these four steps:

1. **Set `CORS_ORIGINS` to your frontend URLs**  
   Specify a comma-separated list of exact origins without trailing slashes: `https://notebook.example.com,https://admin.example.com`. Do not include paths or wildcards unless you intend to allow all origins.

2. **Restart the API process**  
   The environment variable is parsed only at module load time. Changes require a full restart or redeployment of the container to take effect.

3. **Verify startup logs**  
   Check that the application logs contain the line "CORS allowed origins:" followed by your specific domains, confirming the configuration was recognized.

4. **Test browser requests**  
   Make a request from your frontend application and inspect the response headers. You should see `Access-Control-Allow-Origin: https://your-domain.com` rather than `*`.

## Example Production Environment File

Place this configuration at the root of your repository or inject these values through your container orchestration platform:

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

```

According to the documentation in [`docs/5-CONFIGURATION/environment-reference.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/environment-reference.md) (lines 73-78), this variable accepts a comma-separated list of allowed origins. The security guidelines in [`docs/7-DEVELOPMENT/security.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/7-DEVELOPMENT/security.md) (lines 149-157) explicitly recommend restricting this setting before production deployment.

## Summary

- **Never use the default wildcard (`*`) in production.** The `CORS_ORIGINS` environment variable defaults to allowing all origins if unset, which creates security vulnerabilities.
- **Use comma-separated exact URLs.** The parser in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) splits on commas and strips whitespace, supporting multiple frontend domains.
- **Restart required.** Changes to `CORS_ORIGINS` only take effect after restarting the API process.
- **Verify through logs.** Check for the "CORS allowed origins:" startup message to confirm your configuration is active.

## Frequently Asked Questions

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

If you omit this variable, the API defaults to `["*"]` and logs a warning during startup. While functionally allowing your frontend to connect, this also permits any malicious website to make cross-origin requests to your API, potentially exposing sensitive data or allowing unauthorized actions if authentication tokens are present.

### Can I use wildcards or patterns in the `CORS_ORIGINS` value?

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. You must specify exact origins such as `https://app.example.com`. If you need to support multiple subdomains, list each one explicitly separated by commas.

### Do I need to restart the API after changing `CORS_ORIGINS`?

Yes. The environment variable is read and parsed only when the module loads during startup. The `CORS_ALLOWED_ORIGINS` constant is set at this time and passed to the middleware. To apply new values, you must restart the Python process or redeploy your container.

### Where is the CORS middleware registered in the codebase?

The middleware is attached in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) at lines 88-95 using `app.add_middleware(CORSMiddleware, ...)`. This registration occurs after the `_parse_cors_origins` function processes your environment variable, ensuring the middleware receives the correct list of allowed origins immediately upon application startup.