# Reverse Proxy Configuration Guide for Readur This guide covers various deployment scenarios for running Readur behind reverse proxies with configurable ports. ## Table of Contents - [Port Configuration](#port-configuration) - [Docker Deployment](#docker-deployment) - [Nginx Configuration](#nginx-configuration) - [Traefik Configuration](#traefik-configuration) - [Apache Configuration](#apache-configuration) - [Caddy Configuration](#caddy-configuration) - [Common Scenarios](#common-scenarios) - [Troubleshooting](#troubleshooting) ## Port Configuration Readur supports flexible port configuration through environment variables: ### Server Port Configuration ```bash # Method 1: Full address specification SERVER_ADDRESS=0.0.0.0:3000 # Method 2: Separate host and port (recommended) SERVER_HOST=0.0.0.0 SERVER_PORT=3000 ``` ### Development Port Configuration For frontend development with Vite: ```bash # Backend API port BACKEND_PORT=8000 # Frontend dev server port CLIENT_PORT=5173 ``` ## Docker Deployment ### Basic Docker Run ```bash # Run on custom port 3000 docker run -d \ -e SERVER_PORT=3000 \ -e DATABASE_URL=postgresql://user:pass@host/db \ -e JWT_SECRET=your-secret-key \ -p 3000:3000 \ readur:latest ``` ### Docker Compose with Custom Ports ```yaml version: '3.8' services: readur: image: ghcr.io/readur/readur:main environment: SERVER_PORT: 3000 DATABASE_URL: postgresql://readur:readur@postgres/readur JWT_SECRET: ${JWT_SECRET} ports: - "3000:3000" ``` ## Nginx Configuration ### Basic Reverse Proxy ```nginx server { listen 80; server_name readur.example.com; location / { proxy_pass http://localhost:3000; 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; # Large file uploads for documents client_max_body_size 100M; # Extended timeouts for OCR processing proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; } } ``` ### SSL Configuration ```nginx server { listen 443 ssl http2; server_name readur.example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { proxy_pass http://localhost:3000; 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 https; # WebSocket support (future feature) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } ``` ### Subpath Configuration To serve Readur under a subpath like `/readur/`: ```nginx location /readur/ { # Remove prefix when passing to backend rewrite ^/readur/(.*) /$1 break; proxy_pass http://localhost:3000; 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 X-Forwarded-Prefix /readur; } ``` ## Traefik Configuration ### Docker Labels ```yaml version: '3.8' services: readur: image: ghcr.io/readur/readur:main environment: SERVER_PORT: 3000 labels: - "traefik.enable=true" - "traefik.http.routers.readur.rule=Host(`readur.example.com`)" - "traefik.http.routers.readur.tls=true" - "traefik.http.routers.readur.tls.certresolver=letsencrypt" - "traefik.http.services.readur.loadbalancer.server.port=3000" # Middleware for large uploads - "traefik.http.middlewares.readur-upload.buffering.maxRequestBodyBytes=104857600" - "traefik.http.routers.readur.middlewares=readur-upload" ``` ### Static Configuration ```yaml # traefik.yml http: routers: readur: rule: "Host(`readur.example.com`)" service: readur tls: certResolver: letsencrypt middlewares: - readur-headers services: readur: loadBalancer: servers: - url: "http://localhost:3000" healthCheck: path: /api/health interval: 30s middlewares: readur-headers: headers: customRequestHeaders: X-Forwarded-Proto: https ``` ## Apache Configuration ### Basic Reverse Proxy ```apache ServerName readur.example.com ProxyRequests Off ProxyPreserveHost On ProxyPass / http://localhost:3000/ ProxyPassReverse / http://localhost:3000/ # Large file uploads LimitRequestBody 104857600 # Extended timeouts ProxyTimeout 300 ``` ### SSL Configuration ```apache ServerName readur.example.com SSLEngine on SSLCertificateFile /path/to/cert.pem SSLCertificateKeyFile /path/to/key.pem ProxyRequests Off ProxyPreserveHost On ProxyPass / http://localhost:3000/ ProxyPassReverse / http://localhost:3000/ RequestHeader set X-Forwarded-Proto "https" ``` ## Caddy Configuration ### Caddyfile ```caddy readur.example.com { reverse_proxy localhost:3000 { header_up X-Real-IP {remote_host} header_up X-Forwarded-Proto {scheme} # Health check health_uri /api/health health_interval 30s # Extended timeouts transport http { dial_timeout 10s response_header_timeout 300s } } # Large file uploads request_body { max_size 100MB } } ``` ## Common Scenarios ### Multiple Instances with Load Balancing #### Nginx ```nginx upstream readur_pool { least_conn; server readur1:3000 max_fails=3 fail_timeout=30s; server readur:3000 max_fails=3 fail_timeout=30s; server readur3:3000 max_fails=3 fail_timeout=30s; } server { location / { proxy_pass http://readur_pool; # ... other proxy settings } } ``` #### Docker Compose Scale ```bash # Scale to 3 instances docker compose up -d --scale readur=3 ``` ### Blue-Green Deployment ```nginx # nginx.conf upstream readur_blue { server blue:3000; } upstream readur_green { server green:3000; } # Switch between blue and green upstream readur_current { server blue:3000; # Change to green:3000 for switch } server { location / { proxy_pass http://readur_current; } } ``` ### Rate Limiting ```nginx # Define rate limit zones limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m; server { # Apply to API endpoints location /api/ { limit_req zone=api burst=20 nodelay; proxy_pass http://localhost:3000; } # Stricter for auth location /api/auth/ { limit_req zone=auth burst=5 nodelay; proxy_pass http://localhost:3000; } } ``` ## Troubleshooting ### Common Issues 1. **502 Bad Gateway** - Check if Readur is running on the configured port - Verify `SERVER_PORT` environment variable - Check container logs: `docker logs readur` 2. **Large File Upload Failures** - Increase `client_max_body_size` in nginx - Adjust `MAX_FILE_SIZE_MB` in Readur config - Check proxy timeout settings 3. **WebSocket Connection Issues** (future feature) - Ensure `proxy_http_version 1.1` is set - Include Upgrade and Connection headers 4. **CORS Errors** - Readur includes CORS middleware - Ensure `X-Forwarded-Proto` header is set correctly - Check if frontend URL matches expected origin ### Health Check Monitoring ```bash # Direct health check curl http://localhost:3000/api/health # Through reverse proxy curl https://readur.example.com/api/health # Expected response {"status":"ok"} ``` ### Debug Headers Add these to your reverse proxy for debugging: ```nginx add_header X-Proxy-Debug "nginx" always; add_header X-Upstream-Addr $upstream_addr always; add_header X-Upstream-Status $upstream_status always; ``` ## Security Recommendations 1. **Always use HTTPS** in production 2. **Implement rate limiting** to prevent abuse 3. **Set security headers**: ```nginx add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; ``` 4. **Restrict access** to health check endpoint if needed: ```nginx location /api/health { allow 10.0.0.0/8; deny all; proxy_pass http://localhost:3000; } ``` 5. **Use strong JWT secrets** and rotate them regularly 6. **Monitor access logs** for suspicious activity ## Performance Optimization 1. **Enable caching** for static assets: ```nginx location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { proxy_pass http://localhost:3000; expires 1y; add_header Cache-Control "public, immutable"; } ``` 2. **Use HTTP/2** for better performance 3. **Enable gzip compression**: ```nginx gzip on; gzip_types text/plain text/css application/json application/javascript; gzip_min_length 1000; ``` 4. **Configure connection pooling** for database 5. **Use CDN** for static assets in production