docs: add reverse proxy setup instructions for HTTPS deployment

- Add Step 11 with Caddy and nginx configuration examples
- Update Step 2.2 to explain VITE_API_URL setup for proxy vs direct access
- Add common fix for API requests returning HTML instead of JSON
- Document the critical requirement to route /api/* before static files

This documents the fixes for HTTPS proxy deployment where API requests
were being served as HTML instead of being forwarded to the backend.
This commit is contained in:
tanyar09 2026-01-29 19:30:22 +00:00
parent fe9dbc77e5
commit e0712ea520

View File

@ -46,6 +46,16 @@ Notes:
- If you manage Postgres on a separate host, you only need `postgresql-client` on this server.
- If you install Postgres locally, install `postgresql` (server) too, not just the client.
### Firewall Rules (One-time setup)
Configure firewall to allow access to the application ports:
```bash
sudo ufw allow 3000/tcp # Admin frontend
sudo ufw allow 3001/tcp # Viewer frontend
sudo ufw allow 8000/tcp # Backend API
```
---
## Fast path (recommended): run the deploy script
@ -60,10 +70,13 @@ chmod +x scripts/deploy_from_scratch.sh
The script will:
- Install system packages (including Redis)
- Configure firewall rules (optional, with prompt)
- Copy `*_example` env files to real `.env` files (if missing)
- Install Python + Node dependencies
- Generate Prisma clients for the viewer
- Create auth DB tables and admin user (idempotent)
- Build frontend applications for production
- Configure PM2 (copy ecosystem.config.js from example if needed)
- Start services with PM2
If you prefer manual steps, continue below.
@ -131,10 +144,19 @@ cp .env_example .env
2. Edit `.env`:
**For direct access (no reverse proxy):**
```bash
VITE_API_URL=http://YOUR_SERVER_IP_OR_DOMAIN:8000
```
**For reverse proxy setup (HTTPS via Caddy/nginx):**
```bash
# Leave empty to use relative paths - API calls will go through the same proxy
VITE_API_URL=
```
**Important:** When using a reverse proxy (Caddy/nginx) with HTTPS, set `VITE_API_URL` to empty. This allows the frontend to use relative API paths that work correctly with the proxy, avoiding mixed content errors.
### 2.3 Viewer env: `/opt/punimtag/viewer-frontend/.env`
1. Copy and rename:
@ -211,10 +233,44 @@ npx tsx scripts/fix-admin-user.ts
---
## Step 6 — Start the services (PM2)
## Step 6 — Build frontends
This repo includes a PM2 config at `ecosystem.config.js`.
Verify the paths/ports match your server, then:
Build the frontend applications for production:
```bash
# Admin frontend
cd /opt/punimtag/admin-frontend
npm run build
# Viewer frontend
cd /opt/punimtag/viewer-frontend
npm run build
```
Note: The admin frontend build creates a `dist/` directory that will be served by PM2.
The viewer frontend build creates an optimized Next.js production build.
---
## Step 7 — Configure PM2
This repo includes a PM2 config template. If `ecosystem.config.js` doesn't exist, copy it from the example:
```bash
cd /opt/punimtag
cp ecosystem.config.js.example ecosystem.config.js
```
Edit `ecosystem.config.js` and update:
- All `cwd` paths to your deployment directory (e.g., `/opt/punimtag`)
- All `error_file` and `out_file` paths to your user's home directory
- `PYTHONPATH` and `PATH` environment variables to match your deployment paths
---
## Step 8 — Start the services (PM2)
Start all services using PM2:
```bash
cd /opt/punimtag
@ -230,7 +286,7 @@ pm2 startup
---
## Step 7 — First-run DB initialization (automatic)
## Step 9 — First-run DB initialization (automatic)
On first startup, the backend will connect to Postgres and create missing tables automatically.
@ -248,7 +304,7 @@ curl -sS http://127.0.0.1:3001/api/health
---
## Step 8 — Open the apps
## Step 10 — Open the apps
- **Admin**: `http://YOUR_SERVER:3000`
- **Viewer**: `http://YOUR_SERVER:3001`
@ -256,8 +312,112 @@ curl -sS http://127.0.0.1:3001/api/health
---
## Step 11 — Reverse Proxy Setup (HTTPS via Caddy/nginx)
If you're using a reverse proxy (Caddy, nginx, etc.) to serve the application over HTTPS, you need to configure it to route `/api/*` requests to the backend **before** serving static files.
### Issue: API requests returning HTML instead of JSON
**Symptom:** Login works but navigation tabs don't show, or API calls return HTML (the frontend's `index.html`) instead of JSON data.
**Cause:** The reverse proxy is serving static files for all requests, including `/api/*` requests, instead of forwarding them to the backend.
### Solution: Configure proxy to route `/api/*` first
The proxy must forward `/api/*` requests to the backend (port 8000) **before** trying to serve static files.
#### Caddy Configuration
Update your Caddyfile on the proxy server:
```caddyfile
your-admin-domain.com {
import security-headers
# CRITICAL: Route API requests to backend FIRST (before static files)
handle /api/* {
reverse_proxy http://YOUR_BACKEND_IP:8000 {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# Proxy everything else to the frontend
reverse_proxy http://YOUR_BACKEND_IP:3000
}
```
**Important:** The `handle /api/*` block **must come before** the general `reverse_proxy` directive.
After updating:
```bash
# Test configuration
caddy validate --config /path/to/Caddyfile
# Reload Caddy
sudo systemctl reload caddy
```
#### Nginx Configuration
```nginx
server {
listen 80;
server_name your-admin-domain.com;
root /opt/punimtag/admin-frontend/dist;
index index.html;
# CRITICAL: API proxy must come FIRST, before static file location
location /api {
proxy_pass http://YOUR_BACKEND_IP:8000;
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;
}
# Serve static files for everything else
location / {
try_files $uri $uri/ /index.html;
}
}
```
After updating:
```bash
# Test configuration
sudo nginx -t
# Reload nginx
sudo systemctl reload nginx
```
### Environment Variable Setup
When using a reverse proxy, ensure `admin-frontend/.env` has:
```bash
VITE_API_URL=
```
This allows the frontend to use relative API paths (`/api/v1/...`) that work correctly with the proxy.
---
## Common fixes
### API requests return HTML instead of JSON (reverse proxy issue)
**Symptom:** Browser console shows API responses are HTML (the frontend's `index.html`) instead of JSON. Login may work but navigation tabs don't appear.
**Solution:**
1. Ensure your reverse proxy (Caddy/nginx) routes `/api/*` requests to the backend **before** serving static files (see Step 11 above).
2. Verify `admin-frontend/.env` has `VITE_API_URL=` (empty) when using a proxy.
3. Rebuild the frontend after changing `.env`: `cd admin-frontend && npm run build && pm2 restart punimtag-admin`
### Viewer `/api/health` says permission denied
Run the provided grant script on the DB server (as a privileged Postgres user):