Jaehong Jung

RegularExpression Path Matching in HTTPRoute

Kubernetes Gateway API and HTTPRoute

Kubernetes Gateway API is a collection of resources that model service networking in Kubernetes. Among them, HTTPRoute is the resource that defines HTTP routing rules for matching requests and forwarding them to backend services.

One of the most important things when working with HTTPRoute is understanding how multiple HTTPRoute rules get merged when attached to a single Gateway.

When multiple HTTPRoutes are attached to a single Gateway, the Proxy or LoadBalancer must assign priority in the following order:

  1. Exact path match
  2. Prefix path match (longer character count wins)
    • PathPrefix: /col does not route to /color
  3. RegularExpression path match (Istio-specific)
    • Istio uses RE2 syntax (e.g. .* matches any character)
  4. Method match
  5. Largest number of header matches
  6. Largest number of query param matches

Istio Source Code: How Path Match Priority Works

Istio processes Gateway API-based HTTPRoute path matching rules with the following priority logic in its source code.

createURIMatch()

This function converts Gateway API path match types into Istio’s internal StringMatch representation:

func createURIMatch(match k8s.HTTPRouteMatch) (*istio.StringMatch, *ConfigError) {
    tp := k8s.PathMatchPathPrefix
    if match.Path.Type != nil {
        tp = *match.Path.Type
    }
    dest := "/"
    if match.Path.Value != nil {
        dest = *match.Path.Value
    }
    switch tp {
    case k8s.PathMatchPathPrefix:
        if dest != "/" {
            dest = strings.TrimSuffix(dest, "/")
        }
        return &istio.StringMatch{
            MatchType: &istio.StringMatch_Prefix{Prefix: dest},
        }, nil
    case k8s.PathMatchExact:
        return &istio.StringMatch{
            MatchType: &istio.StringMatch_Exact{Exact: dest},
        }, nil
    case k8s.PathMatchRegularExpression:
        return &istio.StringMatch{
            MatchType: &istio.StringMatch_Regex{Regex: dest},
        }, nil
    default:
        return nil, &ConfigError{...}
    }
}

getURIRank()

This function assigns a numeric rank to each match type. The higher the return value, the higher the priority:

// getURIRank ranks a URI match type. Exact > Prefix > Regex
func getURIRank(match *istio.HTTPMatchRequest) int {
    if match.Uri == nil {
        return -1
    }
    switch match.Uri.MatchType.(type) {
    case *istio.StringMatch_Exact:
        return 3
    case *istio.StringMatch_Prefix:
        return 2
    case *istio.StringMatch_Regex:
        return 1
    }
    return -1
}

sortHTTPRoutes()

The sorting function uses getURIRank to order routes by priority:

func sortHTTPRoutes(routes []*istio.HTTPRoute) {
    sort.SliceStable(routes, func(i, j int) bool {
        // ...
        r1, r2 := getURIRank(m1), getURIRank(m2)
        len1, len2 := getURILength(m1), getURILength(m2)
        switch {
        case r1 != r2:
            return r1 > r2 // higher priority first
        case len1 != len2:
            return len1 > len2
        case (m1.Method == nil) != (m2.Method == nil):
            return m1.Method != nil
        case len(m1.Headers) != len(m2.Headers):
            return len(m1.Headers) > len(m2.Headers)
        default:
            return len(m1.QueryParams) > len(m2.QueryParams)
        }
    })
}

The key takeaway: Exact(3) > Prefix(2) > Regex(1). If a PathPrefix that satisfies the route is declared, RegularExpression will never be matched.

The Kubernetes Gateway API spec itself does not define the priority of “RegularExpression” path matches — it is left up to each implementation to decide. This means users have no choice but to follow how their specific implementation handles it. In Istio’s case, getURIRank enforces the URI match type priority as Exact > Prefix > Regex. Therefore, no matter how complex your Regex is, if a matching PathPrefix exists, PathPrefix match takes precedence.

For example, given:

  1. PathPrefix: /api/v1/ -> backend-service
  2. RegularExpression: /api/v1/hooks/.*/callback -> webhook-handler

A request to /api/v1/hooks/provider/callback will always be matched by PathPrefix first, and the RegularExpression rule will never apply.

If you need to use RegularExpression, you should use RegularExpression for all routes within the same hostname group in the gateway to prevent unexpected behavior (e.g. RegularExpression: /.*).

Practical Example: Gateway/Waypoint Separation

(1) Gateway: Broad routing with PathPrefix

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: public-ingress-route
spec:
  parentRefs:
    - name: istio-public-ingress
      namespace: istio-gateway
  hostnames:
    - "api.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: "/"
      backendRefs:
        - name: backend-svc
          port: 8080

(2) Waypoint: Fine-grained routing with RegularExpression

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backend-svc-waypoint
spec:
  parentRefs:
    - kind: Service
      name: backend-svc
  rules:
    - matches:
        - path:
            type: RegularExpression
            value: "/.*"
      backendRefs:
        - name: backend-svc
          port: 8080
    - matches:
        - path:
            type: RegularExpression
            value: "/api/v1/hooks/.*/callback"
      backendRefs:
        - name: webhook-handler
          port: 8080

Tips on Waypoint Rules and Envoy Config

  1. When waypoint.rules is blank (empty), default rules are populated to forward traffic to the backend-svc Envoy cluster.
  2. If only the /api/v1/hooks/.*/callback RegularExpression rule exists, only requests matching that condition will be routed — all other requests will return 404 NOT FOUND.
  3. Both "/.*" and "/api/v1/hooks/.*/callback" must exist to achieve the expected behavior. Since Waypoint and Gateway are bound to separate Envoy instances, Waypoint rules are not affected by Gateway’s PathPrefix rules.

Conclusion

  • Istio enforces PathPrefix > RegularExpression priority at the source code level.
  • In practice, it is recommended to clearly separate Gateway and Waypoint for different routing needs.
  • If you mix PathPrefix and Regex within the same hostname in an HTTPRoute without understanding this structure, PathPrefix will silently consume all matching traffic — a common pitfall.