13 KiB
Gitea over FIPS: /git/ Sub-Path Fix Plan
Problem statement
Accessing Gitea through the FIPS mesh fails with:
Failed to load asset files from
http://npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud.fips/assets/js/index.js?v=1.24.6. Please make sure the asset files can be accessed
The landing page on the same .fips host loads fine.
Root cause (confirmed on the server)
The FIPS network path is healthy — this is not an MTU/PMTUD black-hole. The failure is a
Gitea sub-path reverse-proxy misconfiguration on the server (VM410 / laantungir.net).
Server facts gathered via SSH (ubuntu@laantungir.net):
-
One Gitea instance runs at
127.0.0.1:3000. -
Gitea config is injected by environment variables in
/etc/systemd/system/gitea.service(these override/var/lib/gitea/conf/app.ini):GITEA__server__DOMAIN=git.laantungir.netGITEA__server__ROOT_URL=https://git.laantungir.net/← root pathGITEA__server__HTTP_PORT=3000
-
The
.fipshost is served by/etc/nginx/conf.d/fips-ingress.conf, in theserver { server_name ~^npub1.+\.fips$; ... }block. -
That block proxies Gitea only under
/git/, and it strips the prefix:location ^~ /git/ { rewrite ^/git/?(.*)$ /$1 break; # <-- strips /git/ proxy_pass http://127.0.0.1:3000; proxy_set_header Host git.laantungir.net; ... } location / { root /var/www/html; # <-- static landing page try_files $uri $uri/ =404; }
Why assets 404
- Browser opens
http://<npub>.fips/git/. - nginx strips
/git/and proxies/to Gitea. - Gitea renders HTML, but because
ROOT_URLis root (.../), it emits asset links as root-relative/assets/js/index.js, not/git/assets/.... - Browser requests
http://<npub>.fips/assets/js/index.js. - That path does not match
location ^~ /git/; it falls through tolocation /(static webroot/var/www/html), which has noassetsdirectory → 404. - Gitea's JS bootstrap sees the 404 and throws "Failed to load asset files".
flowchart TD
A[Browser opens npub.fips/git/] --> B[nginx strips /git/ proxies / to Gitea 3000]
B --> C[Gitea ROOT_URL is root so emits /assets/... root-relative]
C --> D[Browser requests npub.fips/assets/js/index.js]
D --> E[No /git/ match falls to location / static webroot]
E --> F[404 Not Found]
F --> G[Gitea JS error Failed to load asset files]
style B fill:#cce5ff
style E fill:#ffcccc
style F fill:#ffcccc
Key asymmetry vs other proxied services
The sibling location ^~ /relay/ and location ^~ /blossom/ blocks also strip their prefix —
and that is fine for them, because those backends do not generate self-referential absolute
URLs from a configured base. Gitea does (assets, redirects, cookies are derived from
ROOT_URL). Therefore Gitea's /git/ block must be configured differently: the prefix must
be preserved and ROOT_URL must include /git/.
Roadmap context
The operator is consolidating subdomains into apex sub-paths:
relay.laantungir.net→laantungir.net/relaygit.laantungir.net→laantungir.net/git
So the destination for Gitea is the /git/ sub-path on the apex host, and equivalently
<npub>.fips/git/ on the mesh. This makes the path-based /git/ mount the correct, permanent
design — not a throwaway workaround. Fixing it now is a down-payment on the end state.
The fix (two paired changes)
For Gitea to live correctly at /git/ on any host, two things must be true together:
- Gitea knows its base path:
ROOT_URLends in/git/→ Gitea then emits assets as/git/assets/...(STATIC_URL_PREFIXfollowsROOT_URL). - nginx preserves the prefix:
proxy_passpasses/git/through (do not strip it), so the prefixed asset/redirect requests reach Gitea.
flowchart TD
A[Browser opens host/git/] --> B[nginx location ^~ /git/ proxy_pass keeps /git/ prefix]
B --> C[Gitea ROOT_URL ends in /git/]
C --> D[Gitea emits /git/assets/...]
D --> E[Browser requests host/git/assets/js/index.js]
E --> F[Matches /git/ location proxies to Gitea]
F --> G[200 OK assets load]
style B fill:#cce5ff
style F fill:#d4edda
style G fill:#d4edda
Single-ROOT_URL consideration
Gitea supports only one ROOT_URL. The transition has two front doors that must both serve
Gitea at the same sub-path /git/:
https://laantungir.net/git/(clearnet — the future)http://<npub>.fips/git/(FIPS — interim and ongoing)
Because the path component is identical (/git/), one ROOT_URL ending in /git/ produces
correct root-relative asset links (/git/assets/...) on both hosts. The only host-specific
concern is absolute links/redirects (login flows, some redirects) and cookie domain, which
Gitea derives from ROOT_URL host + forwarded headers.
Two ways to handle the host portion during transition:
- Simple (recommended to start):
ROOT_URL=https://laantungir.net/git/. Asset loading works on.fipsbecause asset paths are root-relative. Some absolute redirects may point atlaantungir.net; acceptable for a transition. - Mesh-pure (optional polish): add
sub_filteron the.fipsvhost to rewritehttps://laantungir.net/git→http://<npub>.fips/gitin responses, so.fipssessions never bounce to clearnet.
Implementation
All paths below are on the server
ubuntu@laantungir.net(VM410). Make timestamped backups before editing (the server already follows this convention).
Step 1 — Set Gitea ROOT_URL to end in /git/
Edit /etc/systemd/system/gitea.service:
# BEFORE
Environment=GITEA__server__ROOT_URL=https://git.laantungir.net/
# AFTER
Environment=GITEA__server__ROOT_URL=https://laantungir.net/git/
Notes:
DOMAIN/SSH_DOMAINcan remaingit.laantungir.netuntil the subdomain is removed; they do not affect asset paths. (At final cutover, setDOMAIN=laantungir.net.)STATIC_URL_PREFIXis unset, so it defaults to followROOT_URL→/git/assets/.... Do not set a conflictingSTATIC_URL_PREFIX.
Apply:
sudo systemctl daemon-reload
sudo systemctl restart gitea
Step 2 — Fix the .fips nginx vhost /git/ block (preserve prefix)
Edit /etc/nginx/conf.d/fips-ingress.conf, inside server { server_name ~^npub1.+\.fips$; ... }:
# BEFORE
location ^~ /git/ {
rewrite ^/git/?(.*)$ /$1 break; # strips prefix <-- REMOVE
proxy_pass http://127.0.0.1:3000; # no trailing /git/
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host git.laantungir.net;
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 http;
proxy_cache_bypass $http_upgrade;
}
location = /git { return 301 /git/; }
# AFTER
location ^~ /git/ {
# Preserve the /git/ prefix toward Gitea (do NOT strip it).
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host; # forward the .fips 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 http;
proxy_cache_bypass $http_upgrade;
client_max_body_size 512m; # allow git pushes / large uploads
}
location = /git { return 301 /git/; }
Key changes:
- Remove the
rewrite ^/git/?(.*)$ /$1 break;line so the/git/prefix is preserved. - With
location ^~ /git/+proxy_pass http://127.0.0.1:3000;(no URI onproxy_pass), nginx forwards the original URI including/git/. (Do not add a trailing path toproxy_passhere, or nginx will rewrite the matched location prefix.) - Consider
Host $hostso Gitea/forwarded headers reflect the.fipshost. If any Gitea host allow-list rejects it, fall back toHost git.laantungir.net(assets still work either way since they are root-relative under/git/). - Add
client_max_body_sizefor git over HTTP and uploads.
Step 3 (optional) — Keep .fips sessions on the mesh via sub_filter
If absolute links/redirects to https://laantungir.net/git are undesirable for .fips users,
add inside the same /git/ location:
proxy_set_header Accept-Encoding ""; # required for sub_filter to see bodies
sub_filter_once off;
sub_filter_types text/html application/javascript application/json;
sub_filter "https://laantungir.net/git" "http://$host/git";
This is a transition convenience and can be removed at final cutover.
Step 4 — Validate and reload
sudo nginx -t
sudo systemctl reload nginx
# gitea already restarted in Step 1
Step 5 — Add the clearnet laantungir.net/git mount (target state)
This mirrors the .fips block on the apex laantungir.net server blocks (HTTP→HTTPS and the
443 block) in /etc/nginx/conf.d/default.conf. Add to the laantungir.net server block(s):
location ^~ /git/ {
proxy_pass http://127.0.0.1:3000; # preserve /git/ prefix
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host; # laantungir.net
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_cache_bypass $http_upgrade;
client_max_body_size 512m;
}
location = /git { return 301 /git/; }
With ROOT_URL=https://laantungir.net/git/, this serves Gitea cleanly at the apex sub-path with
no rewriting. The existing static landing page remains at /.
Step 6 — Final subdomain removal (later, separate change)
When ready to delete git.laantungir.net:
- Confirm
laantungir.net/git/and<npub>.fips/git/both fully work. - Set
GITEA__server__DOMAIN=laantungir.net(andSSH_DOMAINper SSH plan). - Remove the
git.laantungir.netserverblocks from nginx; reload. - Remove any
sub_filtertransition shims. - Repeat the analogous consolidation for
relay.→/relay(note: relay/websocket backends can keep prefix-stripping since they don't emit self-referential base URLs — unlike Gitea).
Verification
Server-side (on laantungir.net, against local nginx)
# .fips host, /git/ landing
curl -sS -D - -o /dev/null -H "Host: npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud.fips" \
http://127.0.0.1/git/ | grep -Ei 'HTTP/|Set-Cookie|Location'
# expect: 200 OK and Set-Cookie: i_like_gitea (Gitea backend reached)
# .fips host, asset under /git/
curl -sS -o /dev/null -w '%{http_code}\n' -H "Host: npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud.fips" \
"http://127.0.0.1/git/assets/js/index.js?v=1.24.6"
# expect: 200
# clearnet apex /git/ (after Step 5)
curl -sS -o /dev/null -w '%{http_code}\n' -H "Host: laantungir.net" \
"http://127.0.0.1/git/assets/js/index.js?v=1.24.6"
# expect: 200 (or 301->https depending on block)
Client-side (from FIPS client VM ai, over the mesh)
HOST='npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud.fips'
# Gitea landing under /git/
curl -6 -sS -D - -o /dev/null "http://$HOST/git/" | grep -Ei 'HTTP/|Set-Cookie'
# expect: 200 OK, Set-Cookie i_like_gitea
# The previously-failing asset, now under /git/
curl -6 -sS -o /dev/null -w '%{http_code} %{size_download}\n' "http://$HOST/git/assets/js/index.js?v=1.24.6"
# expect: 200 and a non-trivial size
# Confirm root-relative asset path Gitea now emits
curl -6 -sS "http://$HOST/git/" | grep -Eo 'src="[^"]*"|href="[^"]*"' | grep '/git/assets' | head
Browser
- Open
http://<npub>.fips/git/(note the trailing/git/). - Clear cache / hard-reload if a stale Gitea HTML referencing root
/assets/...was cached. - The "Failed to load asset files" error should be gone.
Rollback
- nginx: restore the timestamped backup of
fips-ingress.conf(anddefault.conf), thennginx -t && systemctl reload nginx. - Gitea: restore the previous
ROOT_URLline ingitea.service, thensystemctl daemon-reload && systemctl restart gitea.
Notes / distinguishing from the MTU failure mode
- This failure returns a fast HTTP 404 with a small body for assets — the network is fine.
- An MTU/PMTUD black-hole instead presents as small responses succeeding while large transfers
hang/stall or return
000(connection stalls mid-body). If that signature ever appears, investigate TCP MSS clamping for thefips0MTU (1280) onsys-fips, not this nginx/Gitea path.