# Configuring Reverse Proxy with CORS Settings for Open Notebook

> Learn how to configure a reverse proxy with CORS settings for Open Notebook. Control origins via the CORS_ORIGINS environment variable and FastAPI's CORSMiddleware.

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

---

**Open Notebook requires only a single reverse proxy entry pointing to port 8502, with CORS origins controlled via the `CORS_ORIGINS` environment variable parsed in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) and enforced by FastAPI's CORSMiddleware.**

When deploying the `lfnovo/open-notebook` repository behind a reverse proxy, configuring reverse proxy with CORS settings correctly ensures secure browser communication between the Next.js frontend and FastAPI backend. The application uses a single-port architecture where the frontend handles internal API routing, simplifying proxy configuration while requiring proper origin header forwarding.

## Understanding the Single-Port Architecture

Open Notebook runs two services: a FastAPI backend on port 5055 and a Next.js frontend on port 8502. When placing a reverse proxy in front of the application, you only need to forward traffic to the frontend port 8502. The frontend internally routes `/api/*` requests to the backend, eliminating the need for separate proxy routes or path-based routing rules.

## How CORS Is Handled in Open Notebook

### Development vs Production Settings

In [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py), the application reads the `CORS_ORIGINS` environment variable at startup to configure CORS behavior:

- **Development**: When `CORS_ORIGINS` is unset, the API accepts any origin (`"*"`), allowing local development without configuration.
- **Production**: Set `CORS_ORIGINS` to a comma-separated list of allowed origins (e.g., `https://notebook.example.com`). The application logs a warning if this variable is missing in production environments.

### Implementation Details in api/main.py

The CORS implementation in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) uses FastAPI's `CORSMiddleware` with a custom exception handler to ensure CORS headers appear even on error responses:

```python

# From api/main.py - CORSMiddleware configuration

app.add_middleware(
    CORSMiddleware,
    allow_origins=CORS_ALLOWED_ORIGINS,  # Parsed from CORS_ORIGINS env var

    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

```

The custom exception handler injects CORS headers into HTTP error responses, preventing browsers from blocking error details due to missing `Access-Control-Allow-Origin` headers.

## Reverse Proxy Configuration Examples

Configure your reverse proxy to forward the original `Origin` header unchanged and point to port 8502.

### nginx (Recommended)

The following configuration handles WebSocket upgrades for real-time features and sets appropriate body size limits for file uploads:

```nginx
server {
    listen 443 ssl http2;
    server_name notebook.example.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    client_max_body_size 100M;

    location / {
        proxy_pass http://open-notebook:8502;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_cache_bypass $http_upgrade;
    }
}

```

### Caddy (Automatic HTTPS)

```caddy
notebook.example.com {
    reverse_proxy open-notebook:8502 {
        transport http {
            read_timeout 600s
            write_timeout 600s
        }
    }
}

```

### Traefik (Docker Compose)

```yaml
services:
  open-notebook:
    image: lfnovo/open_notebook:v1-latest
    environment:
      - API_URL=https://notebook.example.com
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.notebook.rule=Host(`notebook.example.com`)"
      - "traefik.http.routers.notebook.entrypoints=websecure"
      - "traefik.http.routers.notebook.tls.certresolver=myresolver"
      - "traefik.http.services.notebook.loadbalancer.server.port=8502"

```

## Environment Variables for CORS

Configure these variables in your `.env` file or container environment according to the reference in [`docs/5-CONFIGURATION/environment-reference.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/environment-reference.md):

```dotenv

# .env.example

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

```

- **`CORS_ORIGINS`**: Controls which origins the FastAPI backend accepts. Leave unset only for local development according to the source code in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py).
- **`API_URL`**: Sets the public URL for the Next.js frontend, required when the reverse proxy hides the backend ports.

## Handling Proxy-Generated Errors

While Open Notebook's [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) handles CORS for application errors, your reverse proxy must add CORS headers to errors it generates itself, such as 413 Payload Too Large. In nginx, configure an error page location:

```nginx
error_page 413 = @cors_error;

location @cors_error {
    add_header Access-Control-Allow-Origin "*";
    return 413;
}

```

This ensures browsers can read error messages when uploads exceed the `client_max_body_size` limit.

## Summary

- Point your reverse proxy to port 8502 only; the frontend handles `/api/*` routing internally to the backend on port 5055.
- Set `CORS_ORIGINS` to a comma-separated list of allowed domains in production; leave unset for development wildcard access.
- Ensure the proxy forwards the `Origin` header unchanged so `CORSMiddleware` in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) can validate requests.
- Add CORS headers to proxy-generated error responses (like 413) to prevent browser blocking of error details.
- Reference [`docs/5-CONFIGURATION/reverse-proxy.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/reverse-proxy.md) and [`docs/5-CONFIGURATION/environment-reference.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/environment-reference.md) for additional configuration options.

## Frequently Asked Questions

### Do I need to expose port 5055 through the reverse proxy?

No. The FastAPI backend on port 5055 should never be exposed directly to the internet. Configure your reverse proxy to forward only to port 8502, where the Next.js frontend internally handles API requests to the backend.

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

If `CORS_ORIGINS` is unset, [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) defaults to allowing all origins (`"*"`), which creates a security vulnerability in production. The application logs a startup warning when this variable is missing, but you should explicitly set allowed origins before deploying.

### Why am I getting CORS errors on large file uploads?

If you see CORS errors specifically when uploading large files, your reverse proxy is likely returning a 413 Payload Too Large error without CORS headers. Add an error handler to your proxy configuration to inject `Access-Control-Allow-Origin` headers on proxy-generated errors, or increase the `client_max_body_size` to accommodate your uploads.

### Can I use different domains for the frontend and API?

While the frontend and API run on separate ports internally, they should share the same public domain behind the reverse proxy. The `API_URL` environment variable tells the frontend its public address, but the browser sees all traffic coming from your single domain, eliminating cross-origin issues between the user and the application.