Skip to content

docs: add Caddy reverse proxy configuration examples - #10344

Open
dpage wants to merge 2 commits into
pgadmin-org:masterfrom
dpage:caddy-docs-10334
Open

docs: add Caddy reverse proxy configuration examples#10344
dpage wants to merge 2 commits into
pgadmin-org:masterfrom
dpage:caddy-docs-10334

Conversation

@dpage

@dpage dpage commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds Caddy examples alongside the existing NGINX ones for hosting pgAdmin with Gunicorn or uWSGI, both at the root of a server and under a sub-directory (mirroring the structure of the existing NGINX sections).
  • Documents the two things that must line up for a sub-directory deployment behind any reverse proxy, not just Caddy: the proxy forwarding the request path unmodified with an X-Script-Name header, and a SCRIPT_NAME environment variable set on the pgAdmin process itself (a separate, static mechanism that sets APPLICATION_ROOT/SESSION_COOKIE_PATH).

Related: #10334, where the reported "login succeeds then bounces back to the login page" turned out to be caused by the missing SCRIPT_NAME process environment variable, not by cookie security flags.

Test plan

  • make docs equivalent (targeted sphinx-build) with no new warnings for server_deployment.rst
  • Verified the documented Caddy config end-to-end locally: pgAdmin running under /pgadmin4 behind a local Caddy instance, full browser login → dashboard → reload cycle with no console errors and no redirect loop

Summary by CodeRabbit

  • Documentation
    • Added deployment guidance for running Gunicorn and uWSGI behind Caddy.
    • Included examples for root and /pgadmin4 subdirectory deployments.
    • Documented Unix-socket reverse proxying, path matching, and URL prefix configuration.
    • Clarified that uWSGI deployments with Caddy should use HTTP mode.
    • Added guidance for avoiding socket access issues caused by systemd’s PrivateTmp=true setting.

Adds Caddy examples alongside the existing NGINX ones for hosting
pgAdmin with Gunicorn or uWSGI, at the root of a server and under a
sub-directory. Addresses pgadmin-org#10334, where the root cause turned out to
be that a sub-directory deployment needs both the reverse proxy to
forward the request path unmodified with an X-Script-Name header, and
the SCRIPT_NAME environment variable set on the pgAdmin process itself
so APPLICATION_ROOT/SESSION_COOKIE_PATH are correct.
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 43a0f481-08c5-43a0-b46f-3fe60fe78850

📥 Commits

Reviewing files that changed from the base of the PR and between fe60c94 and fe5e5c1.

📒 Files selected for processing (1)
  • docs/en_US/server_deployment.rst

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


Walkthrough

Added Caddy deployment documentation for Gunicorn and uWSGI. The examples cover root and /pgadmin4 sub-directory deployments, HTTP or Unix sockets, path matching, X-Script-Name, SCRIPT_NAME, and systemd socket restrictions.

Changes

Caddy deployment

Layer / File(s) Summary
Gunicorn Caddy configuration
docs/en_US/server_deployment.rst
Added root and /pgadmin4 sub-directory reverse-proxy instructions for Gunicorn. The examples configure path matching, SCRIPT_NAME, and X-Script-Name. The guidance covers systemd PrivateTmp=true and alternative socket locations or TCP ports.
uWSGI Caddy configuration
docs/en_US/server_deployment.rst
Added uWSGI deployment instructions that use HTTP-socket mode. The sub-directory example mounts /pgadmin4, sets SCRIPT_NAME, preserves the request path, and forwards X-Script-Name.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Merge Risk: ⚪ Minimal · up to fe5e5

The documentation adds Caddy deployment examples and sub-directory proxy guidance without an identified current merge-blocking risk.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding Caddy reverse proxy configuration examples for the documented deployment scenarios.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Caddy's packaged unit sets PrivateTmp=true, so a Gunicorn or uWSGI socket
created in the system /tmp is not visible to Caddy and every request fails
with a 502.
@asheshv

asheshv commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

The core of this is genuinely valuable — thanks for writing it up. I verified the central claim: evaluate_config.py:161-164 reads SCRIPT_NAME from the process environment and uses it to set APPLICATION_ROOT and SESSION_COOKIE_PATH. That really is a separate mechanism from the per-request X-Script-Name handled in pgAdmin4.py:67-72, it really was undocumented, and it does explain the #10334 redirect loop.

Also good: the Caddy examples get HTTPS automatically, which is better than our existing nginx examples, and X-Forwarded-For is handled safely (PROXY_X_FOR_COUNT = 1 reads the rightmost entry, so a client can't spoof it by prepending fake addresses).

The run-feature-tests-pg (16) failure is unrelated flake — a StaleElementReferenceException in the backup/restore Selenium test, on a docs-only change. Ignore it.

A few things to change before this lands:

1. The examples are documented not to work (server_deployment.rst:329-337).
Every example uses /tmp/pgadmin4.sock, and then the note right below explains that Caddy's shipped systemd unit sets PrivateTmp=true, so Caddy can't see that socket and "will report a 502 error for every request." That's backwards — we're shipping four configs that fail out of the box and then telling the reader to edit them. Please use /run/pgadmin4/pgadmin4.sock (or plain 127.0.0.1:5050) in the examples themselves, and turn the note into an explanation of why /tmp is avoided.

2. That PrivateTmp warning only appears in the Gunicorn section (:329) but applies equally to the uWSGI examples that also use /tmp/pgadmin4.sock. Hoist it or cross-reference it.

3. The nginx examples still have the bug this PR documents (:246-252, :292-310).
The SCRIPT_NAME requirement applies to any reverse proxy — your PR description says so — but the existing nginx sub-directory examples were left untouched, so an nginx user walks straight into #10334. Either add SCRIPT_NAME=/pgadmin4 there too, or lift the explanation into one shared note that all four sections point at.

4. Please strip X-Script-Name in the root-directory examples (:311-317).
pgAdmin4.py:67 trusts the X-Script-Name header unconditionally — no trust count, no allowlist, unlike our PROXY_X_* settings. On a root-mounted deployment a client can send X-Script-Name: /evil and redirect every URL pgAdmin generates. To be clear: this hole is pre-existing and not something this PR introduces (nginx's proxy_params doesn't clear it either). But a brand-new documented config is the right place to close it — adding header_up -X-Script-Name to both root-directory reverse_proxy blocks costs nothing. Same argument applies to X-Scheme (pgAdmin4.py:73), which has no trust gate at all.

Minor:

  • :~425-437 — the uWSGI sub-directory example uses --manage-script-name --mount and sets X-Script-Name. I traced it: it works, because the second strip is a no-op. But it's redundant, and the nginx uWSGI example deliberately omits the header. Drop it, or add a sentence saying why it's harmless.
  • :317, 355, 390 — the new examples copy /usr/lib/python3.7/dist-packages/pgadmin4 and --workers=1 from the older nginx sections. Consistent with what's there, but worth refreshing while the file is open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants