fix: add uri strip_prefix /forge to Caddyfile generator — Forgejo routes 404 without it (completes #1080) #1103

Closed
opened 2026-04-21 11:16:22 +00:00 by disinto-admin · 2 comments

Goal

Complete the /forge/ subpath routing that #1080 partially addressed. The merged fix set FORGEJO__server__ROOT_URL=http://forgejo:3000/forge/ but omitted the required uri strip_prefix /forge directive in the Caddy handle block. Result: Forgejo still returns 404 for /forge/* because it doesn't natively re-route prefixed paths — it relies on the reverse proxy to strip the prefix while ROOT_URL governs link generation in rendered HTML.

Evidence

Verified on disinto-dev-box 2026-04-21 after rebuilding edge with current main:

# Forgejo env (correct after #1080)
$ docker exec disinto-forgejo env | grep ROOT_URL
FORGEJO__server__ROOT_URL=http://forgejo:3000/forge/

# But direct probe to forgejo fails for the prefixed path:
$ docker exec disinto-edge curl -sI http://forgejo:3000/forge/
HTTP/1.1 404 Not Found

$ docker exec disinto-edge curl -sI http://forgejo:3000/
HTTP/1.1 200 OK

Caddy forwards /forge/fooforgejo:3000/forge/foo, Forgejo's router doesn't match, returns 404. Adding uri strip_prefix /forge in the Caddy handle block fixed it live:

$ curl -sI http://localhost/forge/
HTTP/1.1 200 OK  # after strip_prefix patched

Same pattern as the already-merged /staging/ strip (#1079). Forgejo's ROOT_URL still governs link generation, so internal HTML links continue to say /forge/* — the round-trip works.

The fix

In lib/generators.sh (canonical Caddyfile generator), locate the /forge/* handle block:

    handle /forge/* {
        reverse_proxy forgejo:3000
    }

Add the strip_prefix line:

    handle /forge/* {
        uri strip_prefix /forge
        reverse_proxy forgejo:3000
    }

Same exact pattern as the staging block already has (post #1079).

Acceptance criteria

  • lib/generators.sh /forge/* handle block includes uri strip_prefix /forge immediately before reverse_proxy
  • After regenerating the Caddyfile and restarting edge: curl http://localhost/forge/ returns 200 (not 404)
  • curl http://localhost/forge/api/v1/version returns 200 with a version JSON response
  • Forgejo login flow: curl -IL http://localhost/forge/user/login returns 200, no redirect loop
  • HTML response contains /forge/-prefixed internal links (confirms ROOT_URL still governs link generation): curl http://localhost/forge/ | grep -c 'href="/forge' > 0
  • tests/smoke-edge-subpath.sh Test 2 passes against live edge
  • No regression on /staging/ (#1079) or /ci/ paths
  • shellcheck clean on lib/generators.sh

Affected files

  • lib/generators.sh/forge/* handle block
  • docker/Caddyfile (if tracked and used as a template) — mirror the change
  • #1080FORGEJO__server__ROOT_URL env change, merged but incomplete. This issue completes it.
  • #1079/staging/ strip_prefix, already merged; same pattern to apply here.
  • Vision #623 — edge subpath routing sprint
## Goal Complete the `/forge/` subpath routing that #1080 partially addressed. The merged fix set `FORGEJO__server__ROOT_URL=http://forgejo:3000/forge/` but omitted the required `uri strip_prefix /forge` directive in the Caddy handle block. Result: Forgejo still returns 404 for `/forge/*` because it doesn't natively re-route prefixed paths — it relies on the reverse proxy to strip the prefix while ROOT_URL governs link generation in rendered HTML. ## Evidence Verified on disinto-dev-box 2026-04-21 after rebuilding edge with current main: ``` # Forgejo env (correct after #1080) $ docker exec disinto-forgejo env | grep ROOT_URL FORGEJO__server__ROOT_URL=http://forgejo:3000/forge/ # But direct probe to forgejo fails for the prefixed path: $ docker exec disinto-edge curl -sI http://forgejo:3000/forge/ HTTP/1.1 404 Not Found $ docker exec disinto-edge curl -sI http://forgejo:3000/ HTTP/1.1 200 OK ``` Caddy forwards `/forge/foo` → `forgejo:3000/forge/foo`, Forgejo's router doesn't match, returns 404. Adding `uri strip_prefix /forge` in the Caddy handle block fixed it live: ``` $ curl -sI http://localhost/forge/ HTTP/1.1 200 OK # after strip_prefix patched ``` Same pattern as the already-merged `/staging/` strip (#1079). Forgejo's ROOT_URL still governs link generation, so internal HTML links continue to say `/forge/*` — the round-trip works. ## The fix In `lib/generators.sh` (canonical Caddyfile generator), locate the `/forge/*` handle block: ``` handle /forge/* { reverse_proxy forgejo:3000 } ``` Add the strip_prefix line: ``` handle /forge/* { uri strip_prefix /forge reverse_proxy forgejo:3000 } ``` Same exact pattern as the staging block already has (post #1079). ## Acceptance criteria - [ ] `lib/generators.sh` `/forge/*` handle block includes `uri strip_prefix /forge` immediately before `reverse_proxy` - [ ] After regenerating the Caddyfile and restarting edge: `curl http://localhost/forge/` returns 200 (not 404) - [ ] `curl http://localhost/forge/api/v1/version` returns 200 with a version JSON response - [ ] Forgejo login flow: `curl -IL http://localhost/forge/user/login` returns 200, no redirect loop - [ ] HTML response contains `/forge/`-prefixed internal links (confirms ROOT_URL still governs link generation): `curl http://localhost/forge/ | grep -c 'href="/forge'` > 0 - [ ] `tests/smoke-edge-subpath.sh` Test 2 passes against live edge - [ ] No regression on `/staging/` (#1079) or `/ci/` paths - [ ] `shellcheck` clean on `lib/generators.sh` ## Affected files - `lib/generators.sh` — `/forge/*` handle block - `docker/Caddyfile` (if tracked and used as a template) — mirror the change ## Related - #1080 — `FORGEJO__server__ROOT_URL` env change, merged but incomplete. This issue completes it. - #1079 — `/staging/` strip_prefix, already merged; same pattern to apply here. - Vision #623 — edge subpath routing sprint
disinto-admin added the
backlog
label 2026-04-21 11:16:22 +00:00
dev-qwen self-assigned this 2026-04-21 11:16:41 +00:00
dev-qwen added
in-progress
and removed
backlog
labels 2026-04-21 11:16:42 +00:00
Collaborator

Blocked — issue #1103

Field Value
Exit reason ci_exhausted
Timestamp 2026-04-21T11:29:30Z
### Blocked — issue #1103 | Field | Value | |---|---| | Exit reason | `ci_exhausted` | | Timestamp | `2026-04-21T11:29:30Z` |
dev-qwen added
blocked
and removed
in-progress
labels 2026-04-21 11:29:31 +00:00
disinto-admin added
backlog
and removed
blocked
labels 2026-04-21 15:11:46 +00:00
dev-qwen was unassigned by disinto-admin 2026-04-21 15:11:47 +00:00
dev-qwen self-assigned this 2026-04-21 15:12:20 +00:00
dev-qwen added
in-progress
and removed
backlog
labels 2026-04-21 15:12:20 +00:00
disinto-admin added
backlog
and removed
in-progress
labels 2026-04-21 15:55:04 +00:00
dev-qwen was unassigned by disinto-admin 2026-04-21 15:55:15 +00:00
dev-bot self-assigned this 2026-04-21 16:06:21 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-21 16:06:21 +00:00
Collaborator

Blocked — issue #1103

Field Value
Exit reason closed_externally
Timestamp 2026-04-21T16:06:57Z
### Blocked — issue #1103 | Field | Value | |---|---| | Exit reason | `closed_externally` | | Timestamp | `2026-04-21T16:06:57Z` |
dev-qwen added
blocked
and removed
in-progress
labels 2026-04-21 16:06:57 +00:00
dev-bot removed their assignment 2026-04-21 16:26:53 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: disinto-admin/disinto#1103
No description provided.