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

CauseWhat it looks likeFirst thing to check
API/dashboard not enabled404 on every /dashboard/ and /api/* request, on every portStatic config: is api.dashboard (or api: {}) set?
Wrong port or entrypoint404 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/ worksRetry with the trailing slash
Secure mode without a routerAPI enabled, api.insecure false or unset, 404 everywhereDoes 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 loadsInspect the actual rule via /api/http/routers
api.basePath set with insecure modeEndpoints 404 at the prefixed path; basePath is silently ignoredAre api.basePath and api.insecure=true both set?
Docker Swarm missing dummy port labelDashboard router exists but 404s on SwarmIs 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: ping is 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.

  1. 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 simply api: {}, which also enables the dashboard. If neither is present, every other step is moot.

  2. Determine which mode you are in. Check whether api.insecure=true is set. This changes where the dashboard lives:

    • Insecure mode: Traefik auto-generates internal routers with hardcoded rules, PathPrefix('/api') for the API and PathPrefix('/') for the dashboard, served on the entrypoint named traefik. 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.
  3. 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 /dashboard without the slash, that alone explains the 404.

  4. 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.
  5. Check what Traefik actually loaded. Query /api/http/routers from 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.

  6. Check for the basePath trap. If you set api.basePath together with api.insecure=true, the base path is silently ignored: the auto-generated internal routers are hardcoded to PathPrefix('/api') and PathPrefix('/') 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.

  7. 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.

  8. 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

SignalWhy it mattersWarning sign
traefik_entrypoint_requests_total{code="404"} per entrypointDistinguishes “no router matched” (config problem) from backend 404s404s concentrated on the dashboard/API entrypoint after a config change
/api/http/routers contentGround truth for what routing rules Traefik loadedDashboard router missing, wrong entrypoints, or unexpected rule
External probe of /dashboard/ and /api/rawdataDetects unintended public exposure (no Prometheus metric exists for this)200 from an external network
traefik_config_last_reload_successConfirms dynamic config (where your secure-mode router lives) is actually being appliedTimestamp 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/pprof exposes 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/routers that the router exists with the rule and entrypoints you expect, and confirm traefik_config_last_reload_success advanced.
  • Test the dashboard during upgrades. Version-specific regressions happen. A smoke check on /dashboard/ and /api/http/routers after 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_total by 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.