Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Grafana By James Joyner IV · · 9 min read

Grafana Error Guide: Subpath Assets & Login 404 — root_url & serve_from_sub_path

Fix Grafana behind a reverse-proxy subpath returning 404 for assets and login — set root_url and serve_from_sub_path correctly, align proxy path handling, and restore the UI under /grafana.

  • #grafana
  • #troubleshooting
  • #errors
  • #reverse-proxy

Overview

When Grafana runs behind a reverse proxy under a subpath (e.g. https://example.com/grafana), it must generate asset and API URLs that include that prefix. Two grafana.ini settings control this: [server] root_url (the public URL) and [server] serve_from_sub_path. If they don’t match the proxy, Grafana emits links to the wrong path and the browser gets 404 for JS/CSS assets and the login page — a blank or unstyled page.

The literal errors you will see (browser network tab / proxy logs):

GET /grafana/public/build/app.<hash>.js  404 (Not Found)
GET /public/build/runtime.<hash>.js  404 (Not Found)   # missing the /grafana prefix
"If you're seeing this Grafana has failed to load its application files"

It occurs immediately on loading Grafana through the proxy: the page is blank/unstyled and login won’t render.

Symptoms

  • Grafana under /grafana shows a blank white page or unstyled login.
  • Browser console/network shows 404 for public/build/*.js and *.css.
  • Assets 404 either with a doubled prefix (/grafana/grafana/...) or a missing prefix (/public/...).
  • Direct access on :3000 works, but the proxied subpath doesn’t.
curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" \
  https://example.com/grafana/public/build/app.js
404 https://example.com/grafana/public/build/app.js

Common Root Causes

1. root_url missing the subpath

root_url doesn’t include /grafana, so Grafana generates asset links at /public/... and the proxy has nothing to route.

[server]
# WRONG for a subpath deployment
root_url = https://example.com/

2. serve_from_sub_path not enabled

root_url has the subpath but serve_from_sub_path = false, so Grafana serves assets from / internally while links point to /grafana — mismatch → 404.

[server]
root_url = https://example.com/grafana/
serve_from_sub_path = false      # should be true here

3. Proxy strips the prefix while serve_from_sub_path is true

If the proxy rewrites /grafana/... to /... (strips the prefix) but Grafana expects to serve from the sub path, the paths double up or mismatch.

4. Trailing-slash / path mismatch in proxy config

A location /grafana without the correct proxy_pass trailing-slash semantics changes whether the prefix is forwarded, producing missing or doubled prefixes.

Diagnostic Workflow

Step 1: Check the effective server config

grep -A2 "^\[server\]" /etc/grafana/grafana.ini | grep -iE "root_url|serve_from_sub_path|domain"
# env-var form:
env | grep -iE "GF_SERVER_ROOT_URL|GF_SERVER_SERVE_FROM_SUB_PATH"

Step 2: See what path Grafana expects for assets

curl -s http://localhost:3000/grafana/api/health -o /dev/null -w "%{http_code}\n"
curl -s http://localhost:3000/api/health -o /dev/null -w "%{http_code}\n"

With serve_from_sub_path = true, the /grafana/... path should answer; without it, only /... does.

Step 3: Trace the failing asset URL through the proxy

curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" \
  https://example.com/grafana/public/build/runtime.js

A missing /grafana prefix in generated links means root_url is wrong; a doubled prefix means the proxy and serve_from_sub_path disagree.

Step 4: Set the canonical subpath config

[server]
protocol = http
http_port = 3000
domain = example.com
root_url = https://example.com/grafana/
serve_from_sub_path = true
systemctl restart grafana-server

Step 5: Align the proxy (do NOT strip the prefix)

Nginx — pass the prefix through unchanged:

# nginx (shown as config)
location /grafana/ {
  proxy_pass http://127.0.0.1:3000;   # no trailing slash -> keeps /grafana/ prefix
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
}

Example Root Cause Analysis

A team moves Grafana behind Nginx at https://ops.example.com/grafana. The page loads blank; the browser console:

GET https://ops.example.com/public/build/app.5f3.js 404 (Not Found)

Note the missing /grafana prefix. The config:

[server]
root_url = https://ops.example.com/
serve_from_sub_path = true

root_url lacks the subpath, so despite serve_from_sub_path = true, Grafana generates asset links at /public/... — which the proxy only serves under /grafana/.... Fix both settings to agree with the proxy:

[server]
root_url = https://ops.example.com/grafana/
serve_from_sub_path = true

And ensure Nginx forwards the prefix (no trailing slash on proxy_pass). After systemctl restart grafana-server, assets load from /grafana/public/... and the UI renders. Root cause: root_url didn’t include the subpath, so generated links dropped the prefix the proxy needed.

Prevention Best Practices

  • Set root_url to the full public URL including the subpath and trailing slash, and serve_from_sub_path = true together — they must agree.
  • Decide one prefix strategy: let Grafana serve the sub path (keep the prefix in the proxy, serve_from_sub_path = true), and don’t also strip it at the proxy.
  • Test the exact asset URL (/grafana/public/build/*.js) with curl after any proxy or config change.
  • Use env vars (GF_SERVER_ROOT_URL, GF_SERVER_SERVE_FROM_SUB_PATH) in containers so the setting is explicit and versioned.
  • Mirror X-Forwarded-Proto/Host headers so redirects and login callbacks use the right scheme/host.
  • See more Grafana guides.

Quick Command Reference

# Effective server config
grep -iE "root_url|serve_from_sub_path|domain" /etc/grafana/grafana.ini
env | grep -iE "GF_SERVER_ROOT_URL|GF_SERVER_SERVE_FROM_SUB_PATH"

# Does the subpath answer?
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/grafana/api/health
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/api/health

# Trace a failing asset through the proxy
curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" \
  https://example.com/grafana/public/build/runtime.js

# Canonical subpath config (grafana.ini)
#  [server]
#  root_url = https://example.com/grafana/
#  serve_from_sub_path = true
systemctl restart grafana-server

Conclusion

Subpath 404s for assets and login mean Grafana’s generated URLs don’t match the path the reverse proxy serves. Typical root causes:

  1. root_url missing the /grafana subpath, so links drop the prefix.
  2. serve_from_sub_path not enabled while root_url has the subpath.
  3. The proxy stripping the prefix while Grafana expects to serve the sub path (doubled/mismatched paths).
  4. Trailing-slash proxy_pass semantics changing whether the prefix is forwarded.

Set root_url (with subpath + trailing slash) and serve_from_sub_path = true to agree, and make the proxy forward the prefix unchanged; then curl the exact asset URL to confirm it returns 200.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.