You opened a browser to what you think is the dashboard address and got a bare “404 page not found”. Or the dashboard HTML loads but every panel is empty because the underlying /api calls all return 404. This almost always comes down to one of three things: the API is not enabled, you are hitting the wrong port or path, or secure mode is on but no router was ever defined for the internal API service.
The dashboard and API do not live on your traffic entrypoints (80/443) by default. They live on a separate internal entrypoint, they must be explicitly enabled in static configuration, the URL requires a trailing slash, and the secure (recommended) way of exposing them requires a dynamic-config router that many setups never create. Any one of those being wrong produces the same unhelpful 404.
What this means
A 404 from Traefik means “no router matched this request”. Traefik returns 404 at the entrypoint level when a request arrives but no router rule matches it, visible in traefik_entrypoint_requests_total{code="404"}, not in any service metric, because no service was ever selected.
The dashboard and API are themselves served through the routing pipeline. In insecure mode Traefik auto-generates internal routers for them; in secure mode you must define the router yourself, pointing at the special internal service api@internal. If that router does not exist, does not match your request (wrong host, wrong path, wrong entrypoint), or the feature is disabled entirely, you get the standard Traefik 404. The same applies to /ping, a separate feature enabled independently with ping: {} or --ping in static config.
One nuance: the dashboard is a frontend that calls the API. If the dashboard page loads but the API routes 404, the static assets router matched but the API router did not. That points at router rule logic, not at whether the feature is enabled.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| API/dashboard not enabled | 404 on every /dashboard/ and /api/* request, on every port | Static config: is api.dashboard (or api: {}) set? |
| Wrong port or entrypoint | 404 or connection refused on 80/443; works on the internal port (typically 8080) | Which entrypoint is the dashboard bound to? |
| Missing trailing slash | /dashboard returns 404, /dashboard/ works | Retry with the trailing slash |
| Secure mode without a router | API enabled, api.insecure false or unset, 404 everywhere | Does a router for api@internal exist in dynamic config? |
| Router rule logic wrong (OR instead of AND) | Dashboard loads on any Host, or API routes 404 while the page loads | Inspect the actual rule via /api/http/routers |
api.basePath set with insecure mode | Endpoints 404 at the prefixed path; basePath is silently ignored | Are api.basePath and api.insecure=true both set? |
| Docker Swarm missing dummy port label | Dashboard router exists but 404s on Swarm | Is the dummy service port label present? |
Quick checks
All of these are read-only and safe to run on a production instance.
# 1. Is the dashboard reachable on the internal entrypoint at all?
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/dashboard/
# 2. Compare with and without the trailing slash
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/dashboard
# 3. Does the API respond?
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/api/http/routers
# 4. Which ports is Traefik actually listening on?
ss -ltnp | grep traefik
# 5. Is /ping enabled (independent feature, useful reference point)?
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/ping
# 6. If the API responds anywhere, dump what routers Traefik actually loaded
curl -s http://localhost:8080/api/http/routers
Interpretation shortcuts:
- Checks 1 and 3 both 404 on every port: the API is almost certainly not enabled in static config.
- Checks 1 and 3 are 404 on :8080 but return 200 on another port from check 4: wrong entrypoint.
- Check 2 is 404 but check 1 returns 200: trailing slash. Expected behavior, not a bug.
- Check 5 returns 200 but 1 and 3 return 404:
pingis enabled but the API is not. They are independent features. - Check 6 returns a router list with no router for
api@internal: secure mode is missing its router.
How to diagnose it
flowchart TD
A[404 on /dashboard/ or /api/*] --> B{API enabled in static config?}
B -- No --> C[Set api.dashboard=true or api: {}]
B -- Yes --> D{api.insecure=true?}
D -- Yes --> E[Hit the traefik entrypoint, typically :8080, path /dashboard/ with trailing slash]
D -- No --> F{Router for api@internal exists?}
F -- No --> G[Add dynamic-config router to api@internal]
F -- Yes --> H{Rule matches your Host and path?}
H -- No --> I[Fix rule: Host AND grouped PathPrefix OR]
H -- Yes --> J[Check basePath conflict or provider-specific issues]Work through the steps in order. Each step eliminates one layer.
Confirm the API is enabled in static config. This cannot be enabled via dynamic config, labels on another container, or middleware. Look for
api.dashboard=true(CLI flag--api.dashboard=true) or simplyapi: {}, which also enables the dashboard. If neither is present, every other step is moot.Determine which mode you are in. Check whether
api.insecure=trueis set. This changes where the dashboard lives:- Insecure mode: Traefik auto-generates internal routers with hardcoded rules,
PathPrefix('/api')for the API andPathPrefix('/')for the dashboard, served on the entrypoint namedtraefik. You must define that entrypoint yourself; the conventional port is 8080, but it only exists if you declared it. - Secure mode (default): nothing is exposed until you create a router in dynamic config pointing to the service
api@internal.
- Insecure mode: Traefik auto-generates internal routers with hardcoded rules,
Verify the path. The dashboard path is
/dashboard/and the trailing slash is mandatory. There is a redirect from/to/dashboard/, but the Traefik documentation warns not to rely on it. If you bookmarked/dashboardwithout the slash, that alone explains the 404.In secure mode, inspect the router you defined. The recommended rule shape is
Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)). Two failure patterns are common:- Using
||between the Host and the path prefixes instead of&&with a grouped OR. That makes the dashboard match on any Host, which can expose it unintentionally and also produces confusing matches. - The router is attached to the wrong entrypoint, so requests on the port you are testing never reach it.
- Using
Check what Traefik actually loaded. Query
/api/http/routersfrom wherever the API does respond and look for the dashboard/API router: its rule, entrypoints, and status. Traefik silently ignores unknown or malformed labels and annotations, so the config you wrote and the config Traefik loaded can differ without any error. Compare loaded state against intended state.Check for the basePath trap. If you set
api.basePathtogether withapi.insecure=true, the base path is silently ignored: the auto-generated internal routers are hardcoded toPathPrefix('/api')andPathPrefix('/')and never incorporate the prefix. The option is documented as incompatible with insecure mode, but nothing warns you at startup. Endpoints stay at the unprefixed paths.Provider-specific checks. On Docker Swarm, the dashboard router needs a dummy service port label for Swarm’s port detection, for example
traefik.http.services.dummy-svc.loadbalancer.server.port=9999; without it the router 404s. On Kubernetes, verify labels or annotations landed on the object Traefik actually watches.Rule out a version regression. If the dashboard broke immediately after an upgrade with no config change, check the version history and issue tracker before assuming your config is wrong. Pin and test upgrades against the dashboard before rolling them out broadly.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
traefik_entrypoint_requests_total{code="404"} per entrypoint | Distinguishes “no router matched” (config problem) from backend 404s | 404s concentrated on the dashboard/API entrypoint after a config change |
/api/http/routers content | Ground truth for what routing rules Traefik loaded | Dashboard router missing, wrong entrypoints, or unexpected rule |
External probe of /dashboard/ and /api/rawdata | Detects unintended public exposure (no Prometheus metric exists for this) | 200 from an external network |
traefik_config_last_reload_success | Confirms dynamic config (where your secure-mode router lives) is actually being applied | Timestamp not advancing after you added the router |
The last row matters in secure mode: the dashboard router is dynamic config. If your provider is disconnected or your file never reloads, the router you just wrote never takes effect and you keep getting 404 no matter how correct the YAML is. Check config freshness before blaming the router rule.
Fixes
Enable the API and dashboard
Static configuration change, requires a restart of Traefik:
# static config
api:
dashboard: true
or api: {}, which enables both API and dashboard. Static config cannot be toggled at runtime, so plan for the restart.
Hit the right entrypoint and path (insecure mode, local/dev only)
Define the internal entrypoint and use it:
# static config
entryPoints:
traefik:
address: ":8080"
api:
insecure: true
dashboard: true
Then browse to http://<host>:8080/dashboard/ with the trailing slash. Treat insecure mode as a local-development convenience, not a production pattern: it exposes the API with no authentication.
Expose it properly (secure mode)
Keep api.insecure off. Add a router pointing to the internal service in dynamic config:
# dynamic config
http:
routers:
dashboard:
rule: "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
service: api@internal
entryPoints:
- traefik
middlewares:
- dashboard-auth
Put authentication (BasicAuth or ForwardAuth) and ideally an IP allowlist in front of it. The API discloses your entire routing table, backend addresses, and configuration; /api/rawdata alone is a complete map of your infrastructure.
Fix router rule logic
If the dashboard page loads but /api/* calls 404, or the dashboard answers on hosts it should not, rewrite the rule as Host(...) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)). Do not chain the three clauses with bare ||.
Resolve basePath conflicts
Do not combine api.basePath with api.insecure=true. If you need the API under a prefix, use secure mode with an explicit router whose rule includes the prefix.
Swarm: add the dummy service port
Add traefik.http.services.dummy-svc.loadbalancer.server.port=9999 to the dashboard router’s service labels so Swarm port detection succeeds.
Prevention
- Never expose the dashboard or API publicly. The API reveals all routes, backend addresses, health status, middleware configuration, and TLS certificate details, and
/debug/pprofexposes Go profiling data. Bind the internal entrypoint to localhost or an internal interface, firewall it, and put auth middleware in front even internally. - Probe from outside periodically. There is no metric for dashboard exposure; detecting it requires an external probe or network policy audit. A 200 on
/dashboard/,/api/rawdata, or/debug/pprof/from an untrusted network is an immediate remediation item. - Use secure mode in production. Insecure mode exists for local development. Production exposure should go through an explicit router with authentication.
- Verify loaded state, not intended state. After any config change touching the dashboard router, confirm via
/api/http/routersthat the router exists with the rule and entrypoints you expect, and confirmtraefik_config_last_reload_successadvanced. - Test the dashboard during upgrades. Version-specific regressions happen. A smoke check on
/dashboard/and/api/http/routersafter each upgrade catches them before you need the dashboard during an incident.
How Netdata helps
- Netdata’s Traefik collector scrapes the Prometheus endpoint and charts
traefik_entrypoint_requests_totalby status code and entrypoint, so you can see 404s isolated to the dashboard/API entrypoint versus 404s on your traffic entrypoints, which have completely different meanings. - Per-code request charts make the “dashboard loads but API 404s” pattern visible: successful responses on the dashboard path alongside 404s on
/api/*. - Config reload metrics show whether a newly added secure-mode router was actually applied, which separates “router rule wrong” from “provider never delivered the config”.
- Correlating entrypoint 404 rate with reload timestamps after a config change tells you immediately whether a change took effect or silently failed.
- Alerting on sustained entrypoint-level 404 rates catches route loss broadly, including the case where a botched change removes the dashboard router you depend on during incidents.
Related guides
- Traefik 404 not found: requests arriving with no matching router
- Traefik 502 Bad Gateway: when the backend is unreachable or returns garbage
- Traefik 503 Service Unavailable: no healthy backends left in the pool
- Traefik 504 Gateway Timeout: the backend is alive but too slow
- Traefik 5xx error rate: telling Traefik-generated errors from backend errors
- Traefik cascading backend failure: how a partial outage becomes a total one
- Traefik health checks pass but requests fail: when the probe lies
- How Traefik actually works in production: a mental model for operators
- Traefik monitoring checklist: the signals every production edge router needs
- Traefik monitoring maturity model: from survival to expert
- Traefik /ping returns 200 while everything is broken: the health-check trap
- Traefik service server up at zero: backend health checks are failing






