Files
proxmox/docs/03-deployment/EXPLORER_FRONTEND_404_FIX_RUNBOOK.md
defiQUG dedb55e05c
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
docs(03-deployment): runbooks and deployment status updates
Made-with: Cursor
2026-03-27 18:48:41 -07:00

6.4 KiB
Raw Blame History

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/*, /health to 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/html for / and SPA paths.
  • explorer-monorepo/scripts/fix-nginx-conflicts-vmid5000.sh — current “conflicts” config: proxies location / 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 Blockscouts own UI.

2. What we confirmed on VMID 5000

  • Custom frontend present: /var/www/html/index.html exists (~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 no root / try_files for 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:

  1. Serves / from /var/www/html (e.g. location = / with root /var/www/html and try_files /index.html =404).
  2. Serves SPA paths (e.g. /address, /tx, /blocks, …) from the same root with try_files $uri $uri/ /index.html.
  3. Keeps /api/, /api/v1/, /api/config/*, /snap/, /health as 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/stats and https://explorer.d-bis.org/api/v2/stats should 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 (via EXPLORER_VM_HOST=root@192.168.11.12).
  • Script updated: fix-nginx-serve-custom-frontend.sh now includes location /api/v1/ (token-aggregation :3001) and location = /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.