Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ See [comparison notes](docs/comparison.md) for when GoMyAdmin is a good fit.
- [Multi-tenancy](docs/multi-tenancy.md)
- [Drop-in adapters](docs/drop-in-adapters.md)
- [Deployment](docs/deployment.md)
- [Reverse proxy deployment](docs/reverse-proxy.md)
- [Supabase/Postgres example](docs/supabase-postgres.md)
- [Troubleshooting](docs/troubleshooting.md)
- [Comparison](docs/comparison.md)
Expand Down
142 changes: 142 additions & 0 deletions docs/reverse-proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Reverse Proxy Deployment

GoMyAdmin can run behind Nginx, Caddy, Traefik, or a platform proxy as long as the public URL, forwarded headers, cookies, upload limits, and timeouts all agree.

The generated backend serves API routes under `/admin/api/`. The generated Next.js frontend serves the admin UI under `/admin`. In production, route both through HTTPS and keep the browser-facing origin stable.

## Public URL and Admin Path

Set the backend `PublicURL` to the externally reachable origin, including scheme and host:

```sh
GOMYADMIN_PUBLIC_URL=https://admin.example.com
```

`server.Config.PublicURL` defaults to `GOMYADMIN_PUBLIC_URL`, then `http://localhost:8080`. GoMyAdmin uses this value for:

- `Access-Control-Allow-Origin`
- OAuth redirect callback URLs such as `/admin/api/auth/oauth/google/callback`
- generated local file URLs such as `/admin/api/files/{id}`

Set the generated frontend API URL to the same public origin when the UI and API are served together. If they are split across origins, point it at the public backend origin instead. The development fallback is `http://localhost:8080`, so production deployments should set this explicitly:

```sh
NEXT_PUBLIC_ADMIN_API_URL=https://admin.example.com
```

Avoid publishing the admin UI under one path while sending API requests to another origin unless CORS and cookies are configured intentionally.

## Nginx Example

This example serves the Next.js admin frontend from `127.0.0.1:3000` and the Go backend from `127.0.0.1:8080` under one HTTPS origin.

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

client_max_body_size 25m;

location /admin/api/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}

location /admin/ {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route the /admin entry point too

This Nginx location only matches /admin/..., not /admin itself. The generated Next.js app defines an /admin route and the README/getting-started flow tells users to open /admin, so following this example can leave the documented entry point unproxied unless another server block happens to redirect it. Add an exact /admin redirect or proxy rule so both /admin and /admin/... work.

Useful? React with 👍 / 👎.

proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
}
```

Keep the trailing slash on `location /admin/api/` so API routes continue to match GoMyAdmin's `/admin/api/...` handlers.

## Caddy Example

```caddyfile
admin.example.com {
request_body {
max_size 25MB
}

handle /admin/api/* {
reverse_proxy 127.0.0.1:8080 {
header_up Host {host}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Host {host}
}
}

handle /admin/* {
reverse_proxy 127.0.0.1:3000 {
header_up Host {host}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Host {host}
}
}
}
```

Use `handle` instead of `handle_path` so Caddy does not strip the `/admin/api/` prefix before proxying.

## Cookies and CSRF

GoMyAdmin uses:

- `gomyadmin_session` for the HTTP-only admin session
- `gomyadmin_csrf` for the double-submit CSRF token
- `gomyadmin_oauth` for OAuth state when OAuth is enabled

Production deployments should:

- terminate TLS before requests reach users
- use one stable public origin for UI and API whenever possible
- keep `SameSite=Lax` behavior in mind when splitting UI and API across subdomains
- pass `X-CSRF-Token` on mutating admin API requests
- set `GOMYADMIN_SESSION_SECRET` to a strong private value

If login succeeds but later writes fail with `CSRF_FAILED`, confirm that the browser receives `gomyadmin_csrf`, sends it back in `X-CSRF-Token`, and that the proxy is not stripping cookies or custom headers.

## Upload Limits

File uploads go through `POST /admin/api/files`. Configure the proxy body limit to be at least as large as the maximum upload size you allow in the app or storage adapter.

Recommended checks:

- Nginx: set `client_max_body_size`.
- Caddy: set `request_body max_size`.
- Platform proxies: check request body and timeout limits.
- S3/R2/MinIO: use private buckets and signed URLs for sensitive files.

If uploads fail with `413 Request Entity Too Large`, raise the proxy limit before changing application code.

## Timeouts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mention the backend 10s timeout before recommending 120s

Raising only the proxy timeouts to 120s does not let long exports/uploads run that long in the documented setup: both pkg/server/handler.go and the generated backend install requestTimeout(10 * time.Second), so the backend request context is canceled first. This guidance should call out the application timeout/configuration requirement, otherwise users can follow the deployment guide and still see long operations fail after about 10 seconds.

Useful? React with 👍 / 👎.

Large exports, imports, slow database queries, and file uploads can outlive very short proxy defaults. Start with 120 seconds for `proxy_read_timeout` and `proxy_send_timeout`, then tighten based on production metrics.

Keep database statement timeouts and HTTP proxy timeouts aligned. A proxy timeout shorter than the backend timeout can make successful backend work look like an intermittent network failure to the browser.

## Troubleshooting Checklist

- `GOMYADMIN_PUBLIC_URL` exactly matches the browser-facing origin, including `https://`.
- `NEXT_PUBLIC_ADMIN_API_URL` points to the public backend URL instead of the localhost development fallback.
- `/admin/` reaches the Next.js frontend and `/admin/api/` reaches the Go backend.
- `Host`, `X-Forwarded-Host`, and `X-Forwarded-Proto` are forwarded.
- Login responses set `gomyadmin_session` and `gomyadmin_csrf`.
- Mutating requests include `X-CSRF-Token`.
- The proxy allows `Content-Type`, `X-CSRF-Token`, and `X-Request-ID` headers.
- Upload body limits are high enough for expected files.
- OAuth callback URLs registered with the provider match `/admin/api/auth/oauth/{provider}/callback`.
- Run `gomyadmin doctor` after changing deployment configuration.
Loading