Made-with: Cursor
6.4 KiB
Explorer frontend 404 fix — runbook
Date: 2026-03-02
Issue: Root path (/) at https://explorer.d-bis.org returns 404 "Page not found".
Cause: Nginx proxies / to Blockscout :4000; Blockscout is API-only and has no route for GET /.
Status: ✅ FIXED (2026-03-02). Nginx now serves the custom frontend from /var/www/html for / and SPA paths; /api/v1/ and /api/config/* preserved. All endpoints verified 200.
1. How the UI is supposed to be served
This deployment uses a custom frontend (SolaceScanScout), not the built-in Blockscout web UI:
- Static frontend: Built files under
/var/www/html/on VMID 5000:index.html(main SPA shell, contains "SolaceScanScout")explorer-spa.js,favicon.ico,apple-touch-icon.png,/snap/, etc.
- Blockscout (port 4000): API only. The Phoenix router has no route for
GET /; it serves/api/*and returns 404 for/. - Nginx: Should serve the static frontend for
/and SPA paths, and proxy only/api/,/api/v1/,/api/config/*,/healthto the appropriate backends (Blockscout :4000, token-aggregation :3001, or static config files).
Relevant docs/config:
explorer-monorepo/scripts/fix-nginx-serve-custom-frontend.sh— nginx config that serves/var/www/htmlfor/and SPA paths.explorer-monorepo/scripts/fix-nginx-conflicts-vmid5000.sh— current “conflicts” config: proxieslocation /to :4000 (no static root).explorer-monorepo/scripts/deploy-frontend-to-vmid5000.sh— deploys frontend files and can apply the custom-frontend nginx config.- This runbook replaces ad-hoc 404 notes; use
explorer-monorepo/scripts/above for nginx and deploy. explorer-monorepo/docs/BLOCKSCOUT_START_AND_BUILD.md— Blockscout container/assets; UI in this setup is the custom frontend, not Blockscout’s own UI.
2. What we confirmed on VMID 5000
- Custom frontend present:
/var/www/html/index.htmlexists (~60KB), contains "SolaceScanScout";explorer-spa.js, favicon,/snap/, etc. are present. - Blockscout logs: For
GET /to :4000, Blockscout logs:
Phoenix.Router.NoRouteError, "no route found for GET / (BlockScoutWeb.Router)". So 404 for/is expected when nginx sends/to Blockscout. - Live nginx: HTTPS server block has
location / { proxy_pass http://127.0.0.1:4000; }with noroot/try_filesfor the frontend. So every request to/is proxied to Blockscout and returns 404.
Conclusion: the frontend files are in place; the nginx config is wrong (proxy-only for / instead of serving static files).
3. Fix: make nginx serve the custom frontend for /
Apply a config that, for the HTTPS (and optionally HTTP) server block:
- Serves
/from/var/www/html(e.g.location = /withroot /var/www/htmlandtry_files /index.html =404). - Serves SPA paths (e.g.
/address,/tx,/blocks, …) from the same root withtry_files $uri $uri/ /index.html. - Keeps
/api/,/api/v1/,/api/config/*,/snap/,/healthas they are (proxy or alias).
Option A — Apply the full custom-frontend script (recommended)
From the repo root, from a host that can SSH to the Proxmox node for VMID 5000 (e.g. r630-02):
# Set Proxmox host (r630-02)
export PROXMOX_R630_02=192.168.11.12 # or PROXMOX_HOST_R630_02
# Apply nginx config that serves / and SPA from /var/www/html
cd /home/intlc/projects/proxmox/explorer-monorepo
# Copy script into VM and run (requires pct exec)
EXPLORER_VM_HOST=root@192.168.11.12 bash scripts/apply-nginx-explorer-fix.sh
Or run the fix script inside VMID 5000 (e.g. after copying it in):
# From Proxmox host
pct exec 5000 -- bash /path/to/fix-nginx-serve-custom-frontend.sh
Option B — Manual nginx change (HTTPS server block only)
On VMID 5000, edit /etc/nginx/sites-enabled/blockscout. In the server { listen 443 ... } block, replace the single:
location / {
proxy_pass http://127.0.0.1:4000;
...
}
with something equivalent to:
# Serve custom frontend for root
location = / {
root /var/www/html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
try_files /index.html =404;
}
# SPA paths — serve index.html for client-side routing
location ~ ^/(address|tx|block|token|tokens|blocks|transactions|bridge|weth|watchlist|nft|home|analytics|operator)(/|$) {
root /var/www/html;
try_files /index.html =404;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# All other non-API paths — static files and SPA fallback
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
}
Keep all existing location /api/, location /api/v1/, location /api/config/, location /snap/, location /health blocks unchanged and before the catch-all location / (so API and config still proxy correctly).
Then:
nginx -t && systemctl reload nginx
4. Verify
- From LAN:
curl -sk -H "Host: explorer.d-bis.org" https://192.168.11.140:443/
should return 200 with HTML containing "SolaceScanScout" (or similar), not "Page not found". - Public:
https://explorer.d-bis.org/should show the explorer UI. - API unchanged:
curl -s http://192.168.11.140:4000/api/v2/statsandhttps://explorer.d-bis.org/api/v2/statsshould still return JSON.
5. Summary
| Item | Status |
|---|---|
| How UI is served | Custom static frontend in /var/www/html/ (index.html + SPA); Blockscout :4000 is API-only. |
| Frontend files on VMID 5000 | Present; index.html contains SolaceScanScout. |
Blockscout logs for GET / |
NoRouteError for GET / — expected when nginx proxies / to :4000. |
| Nginx fix | Serve / and SPA paths from root /var/www/html and try_files; proxy only /api/ (and specific locations) to :4000. |
| Script to apply | fix-nginx-serve-custom-frontend.sh or apply-nginx-explorer-fix.sh; or apply the manual snippet above. |
6. Completion (2026-03-02)
- Applied:
apply-nginx-explorer-fix.sh(viaEXPLORER_VM_HOST=root@192.168.11.12). - Script updated:
fix-nginx-serve-custom-frontend.shnow includeslocation /api/v1/(token-aggregation :3001) andlocation = /api/config/token-list/location = /api/config/networks(static JSON) so config and token-aggregation are not lost on re-apply. - Verification: From LAN, all return 200:
/(frontend HTML),/api/config/token-list,/api/config/networks,/api/v2/stats,/api/v1/chains.