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

Kubernetes Error Guide: 'default backend - 404' from ingress-nginx

Fix default backend - 404 from ingress-nginx: resolve missing Ingress rules, ingressClassName mismatch, empty endpoints, wrong pathType, and host header issues.

  • #kubernetes
  • #troubleshooting
  • #errors
  • #ingress

Exact Error Message

A request through an ingress-nginx controller returns the controller’s fallback page:

$ curl -i http://app.example.com/
HTTP/1.1 404 Not Found
Date: Thu, 25 Jun 2026 10:14:02 GMT
Content-Type: text/html
Content-Length: 21
Connection: keep-alive

default backend - 404

Browsers may instead show the styled variant:

404 Not Found
nginx

What the Error Means

default backend - 404 is returned by ingress-nginx’s built-in default backend when no Ingress rule matches the incoming request. The controller picks a backend by matching the request’s Host header and path against the Ingress objects it manages. If nothing matches — or if a matching Ingress points at a Service with no healthy endpoints — the controller routes to its catch-all default backend, which answers every request with this 404.

The key point: the 404 comes from ingress-nginx, not from your application. Your app may be perfectly healthy; the controller simply never forwarded the request to it. This distinguishes it from an application-level 404 (which would carry your app’s own error page and server header).

Common Causes

  • No Ingress rule matches the host or path. There is no rule for the requested Host, or the path prefix does not match.
  • ingressClassName mismatch. The Ingress names a class the controller does not watch, so the controller ignores the Ingress entirely.
  • Empty endpoints. The target Service has no Ready endpoints — selector mismatch, wrong port, or pods not Ready.
  • Wrong path / pathType. Exact vs Prefix mismatch, or a path that does not align with how the app serves routes.
  • Host header mismatch. The client sends a different Host (or none) than the rule’s host:.
  • TLS / SNI mismatch. HTTPS request’s SNI does not match any configured host, so it falls to the default server.
  • Ingress in the wrong namespace. The Ingress and the target Service are in different namespaces; ingress-nginx only resolves Services in the Ingress’s own namespace.
  • rewrite-target misconfiguration. A rewrite/capture-group annotation strips the path so the upstream sees a route it does not serve.

How to Reproduce the Error

Create an Ingress whose host does not match the request:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: nginx
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80
# Request a host the Ingress does NOT define
curl -i -H 'Host: wrong.example.com' http://<INGRESS_LB_IP>/
HTTP/1.1 404 Not Found
...
default backend - 404

No rule matches wrong.example.com, so the controller returns its default backend.

Diagnostic Commands

# Does the Ingress exist, and what class / host / backend does it use?
kubectl get ingress -A
kubectl describe ingress web -n prod
kubectl get ingress web -n prod -o jsonpath='{.spec.ingressClassName}{"\n"}'
# Does the target Service have endpoints?
kubectl get endpoints web -n prod
kubectl get endpointslices -n prod -l kubernetes.io/service-name=web
kubectl get pods -n prod -l app=web -o wide
# What class does the controller watch, and what did it log?
kubectl get ingressclass
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=60 | grep -i 'web\|default backend\|ingress'
# Reproduce with an explicit Host header against the controller
curl -i -H 'Host: app.example.com' http://<INGRESS_LB_IP>/
# For TLS / SNI
curl -ivk https://app.example.com/ --resolve app.example.com:443:<INGRESS_LB_IP>

Step-by-Step Resolution

  1. Confirm the Ingress is picked up by the controller. If describe shows no Address and the controller log never mentions the Ingress, it is being ignored — almost always a class problem.

    kubectl get ingressclass
    kubectl get ingress web -n prod -o jsonpath='{.spec.ingressClassName}{"\n"}'

    Set spec.ingressClassName to the class the controller watches (commonly nginx).

  2. Check the host and path match the request. Make the rule’s host: match the Host header clients send (or remove host: for a catch-all), and verify pathType.

    - path: /
      pathType: Prefix      # Prefix matches /, /api, /api/v1; Exact matches only "/"
  3. Verify the Service has endpoints. An empty ENDPOINTS column means the controller has nothing to route to.

    kubectl get endpoints web -n prod
    NAME   ENDPOINTS   AGE
    web    <none>      6m

    Fix the Service selector/port to match the pods, and ensure the pods pass their readiness probe (only Ready pods become endpoints).

    kubectl get svc web -n prod -o jsonpath='{.spec.selector}{"\n"}'
    kubectl get pods -n prod --show-labels | grep web
  4. Put the Ingress in the Service’s namespace. ingress-nginx resolves the backend Service in the Ingress’s own namespace; move or duplicate the Ingress so both live together.

  5. Fix TLS/SNI. For HTTPS, ensure a tls: host and Secret exist for the requested host; an SNI miss falls through to the default backend.

    tls:
    - hosts: [app.example.com]
      secretName: app-tls
  6. Review rewrite-target. If you use nginx.ingress.kubernetes.io/rewrite-target, confirm the capture group and path leave the upstream a route it serves.

  7. Verify the fix.

    curl -i -H 'Host: app.example.com' http://<INGRESS_LB_IP>/

Prevention and Best Practices

  • Always set spec.ingressClassName explicitly and match it to the controller’s watched class; relying on the deprecated kubernetes.io/ingress.class annotation is error-prone.
  • Keep each Ingress in the same namespace as the Service it targets — cross-namespace backends are not resolved.
  • Gate Services behind readiness probes that reflect real readiness, so pods only become endpoints when they can serve traffic.
  • Use Prefix pathType for path-based routing unless you specifically need Exact, and document any rewrite-target capture groups.
  • Add synthetic checks that curl each public host/path through the LB and alert on default backend - 404, catching mismatches before users do.
  • Standardize TLS host lists and Secrets per host so SNI always matches a configured server. See more in Kubernetes & Helm guides.
  • 503 Service Temporarily Unavailable from ingress-nginx — a matched Ingress whose Service has endpoints that are all failing health checks.
  • endpoints "web" not found / empty endpoints — the selector/readiness sub-cause covered above.
  • ImagePullBackOff — keeps backend pods from ever becoming Ready, so endpoints stay empty; see the ImagePullBackOff guide.
  • node NotReady — backend pods on a NotReady node drop out of endpoints; see the node NotReady guide.

Frequently Asked Questions

How do I know the 404 is from ingress-nginx and not my app? The body is literally default backend - 404 and the Server header is nginx with Content-Length: 21. An application 404 carries your app’s own page and server header. If you see the default backend text, the controller never reached your Service.

My Ingress exists but the controller ignores it — why? The most common reason is an ingressClassName that does not match the class the controller watches. Run kubectl get ingressclass, compare it to the Ingress’s spec.ingressClassName, and check the controller logs — an ignored Ingress is never mentioned there and gets no Address.

The Ingress and Service look correct but I still get the default backend. Check kubectl get endpoints <service>. If it shows <none>, the Service selector does not match any Ready pods, so the controller has no upstream and falls back to the default backend. Fix the selector/port or the pods’ readiness.

Why does curl to the IP work but the hostname returns the default backend? Your rule has a host: that does not match the Host header the request carries. Test with curl -H 'Host: app.example.com'; if that works, fix DNS/the host value so real clients send the matching header, or remove host: for a catch-all rule.

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.