oCoreoCore Docs

Reverse Proxy

Configure Nginx, Caddy, or Traefik as a reverse proxy for oCore with TLS termination and WebSocket support.

A reverse proxy sits in front of oCore to handle TLS termination, route requests to the backend and frontend, and provide additional security layers. Choose the option that fits your infrastructure.

Architecture

Client --> Reverse Proxy (443) --> Frontend (3000)
                               --> Backend API (8080)
Client --> SSH Gateway (2222) [direct, not proxied]

The reverse proxy handles HTTPS for the dashboard and API. The SSH gateway on port 2222 is accessed directly and does not need proxying.

Nginx

Nginx is the most widely deployed option with fine-grained control over every aspect of request handling.

Full Configuration

/etc/nginx/sites-available/ocore
upstream ocore_backend {
    server 127.0.0.1:8080;
    keepalive 32;
}

upstream ocore_frontend {
    server 127.0.0.1:3000;
    keepalive 16;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name ocore.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name ocore.example.com;

    # TLS certificates (managed by Certbot or manually)
    ssl_certificate /etc/letsencrypt/live/ocore.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ocore.example.com/privkey.pem;

    # TLS configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # Security headers
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

    # Backend API
    location /api/ {
        proxy_pass http://ocore_backend;
        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;

        # WebSocket support (for SSE/streaming endpoints)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Increase timeouts for long-running operations
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;

        # Disable buffering for streaming responses
        proxy_buffering off;
    }

    # Frontend dashboard
    location / {
        proxy_pass http://ocore_frontend;
        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;

        # Next.js hot reload (development only)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # Max upload size (for backup uploads, etc.)
    client_max_body_size 512M;
}

Enable the Site

sudo ln -s /etc/nginx/sites-available/ocore /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Caddy

Caddy automatically obtains and renews TLS certificates from Let's Encrypt. It is the simplest option for most deployments.

Caddyfile

/etc/caddy/Caddyfile
ocore.example.com {
    # Backend API
    handle /api/* {
        reverse_proxy localhost:8080 {
            header_up X-Real-IP {remote_host}
            header_up X-Forwarded-Proto {scheme}

            # Flush immediately for SSE/streaming
            flush_interval -1
        }
    }

    # Frontend dashboard
    handle {
        reverse_proxy localhost:3000 {
            header_up X-Real-IP {remote_host}
            header_up X-Forwarded-Proto {scheme}
        }
    }

    # Security headers
    header {
        X-Frame-Options DENY
        X-Content-Type-Options nosniff
        Referrer-Policy strict-origin-when-cross-origin
        Strict-Transport-Security "max-age=63072000; includeSubDomains"
    }
}

Start Caddy

sudo systemctl enable caddy
sudo systemctl start caddy

Caddy automatically obtains a TLS certificate for ocore.example.com on first request. No additional certificate configuration is needed.

Traefik

Traefik integrates natively with Docker and configures routing through container labels.

Static Configuration

/etc/traefik/traefik.yml
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@example.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

providers:
  docker:
    exposedByDefault: false

Docker Labels

Add labels to your docker-compose.prod.yml services:

docker-compose.prod.yml (Traefik labels)
services:
  backend:
    # ... existing config
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.ocore-api.rule=Host(`ocore.example.com`) && PathPrefix(`/api`)"
      - "traefik.http.routers.ocore-api.entrypoints=websecure"
      - "traefik.http.routers.ocore-api.tls.certresolver=letsencrypt"
      - "traefik.http.services.ocore-api.loadbalancer.server.port=8080"

  frontend:
    # ... existing config
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.ocore-frontend.rule=Host(`ocore.example.com`)"
      - "traefik.http.routers.ocore-frontend.entrypoints=websecure"
      - "traefik.http.routers.ocore-frontend.tls.certresolver=letsencrypt"
      - "traefik.http.services.ocore-frontend.loadbalancer.server.port=3000"
      # Lower priority so /api routes match the backend first
      - "traefik.http.routers.ocore-frontend.priority=1"

  traefik:
    image: traefik:v3
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik_letsencrypt:/letsencrypt
    restart: unless-stopped

volumes:
  traefik_letsencrypt:
    name: traefik_letsencrypt

Choosing a Reverse Proxy

FeatureNginxCaddyTraefik
Automatic TLSNo (use Certbot)YesYes
ConfigurationFile-basedFile-basedDocker labels
WebSocket supportManual configBuilt-inBuilt-in
Learning curveModerateLowModerate
Resource usageLowLowLow-Moderate
Best forExisting Nginx infrastructureNew deployments, simplicityDocker-native environments

Recommendation: Use Caddy for new deployments -- automatic TLS with zero certificate management. Use Nginx if you already have it in your stack. Use Traefik if you run multiple Docker services and want label-based routing.

Verifying the Proxy

After configuring your reverse proxy, verify it is working:

# Check HTTPS is working
curl -I https://ocore.example.com

# Check the API is accessible
curl https://ocore.example.com/api/health

# Check security headers
curl -I https://ocore.example.com | grep -E "Strict-Transport|X-Frame|X-Content"
Was this page helpful?