#13900·traefik

kubernetesIngressNGINX: location ^~ modifier silently fails to match paths in server-snippet

Author: asouchangCreated Sep 14, 2026Updated Sep 17, 2026
Labelskind/bug/confirmedcontributor/wantedarea/provider/k8s/ingress-nginx

Welcome!

  • Yes, I've searched similar issues on GitHub and didn't find any.
  • Yes, I've searched similar issues on the Traefik community forum and didn't find any.

What did you do?

Used kubernetesIngressNGINX provider with an Ingress containing an NGINX server-snippet using the standard ^~ (best prefix match) location modifier to block internal paths:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/server-snippet: |
      location ^~ /internal {
        return 404;
      }
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: test-service
                port:
                  number: 80

Sent an HTTP request to GET /internal.

What did you see instead?

The request to /internal was not blocked and fell through to the catch-all path: / backend. No error or warning was produced during Ingress parsing or at runtime.

Root Cause in Source Code:

In pkg/middlewares/ingressnginx/snippet/collector.go:

  1. createLocationCollectable() handles 2 parameters when a modifier is present:

    pathPattern := params[0].String()
    if len(params) == 2 {
        pathPattern += params[1].String()
    }
    

    For location ^~ /internal, params[0] is "^~" and params[1] is "/internal", making pathPattern = "^~/internal".

  2. buildLocationMatcher(pathPattern) checks for ~*, ~, and =, but omits ^~:

    func buildLocationMatcher(pathPattern string) (locationMatcher, error) {
        if pattern, ok := strings.CutPrefix(pathPattern, "~*"); ok { ... }
        if pattern, ok := strings.CutPrefix(pathPattern, "~"); ok { ... }
        if exact, ok := strings.CutPrefix(pathPattern, "="); ok { ... }
    
        // Fallback: Prefix match
        return func(req *http.Request) bool {
            return strings.HasPrefix(req.URL.Path, pathPattern)
        }, nil
    }
    
  3. Because "^~/internal" does not start with ~ or =, it falls into the prefix match fallback: strings.HasPrefix(req.URL.Path, "^~/internal").

  4. Since request paths never start with "^~", matcher(req) silently evaluates to false for every request, causing the block to never execute.

What version of Traefik are you using?

v3.7.5

What is your environment & configuration?

  • Kubernetes with kubernetesIngressNGINX provider enabled (--providers.kubernetesingressnginx=true, --providers.kubernetesingressnginx.allowSnippetAnnotations=true).
  • Standard networking.k8s.io/v1 Ingress with nginx.ingress.kubernetes.io/server-snippet.