Skip to content

Traditional Router Matching #14839

@bakjos

Description

@bakjos

Is there an existing issue for this?

  • I have searched the existing issues

Kong version ($ kong version)

3.13

Current Behavior

When multiple path regexes match the same request URI, only the first matching path is used to select candidates. The reducer does reduced_candidates = category.routes_by_uris[ctx.hits.uri], so it only returns routes for that one path string. As a result:

  1. The first path to match is the auth route's path (e.g. /api(/.*)), because routes with headers are ordered before routes without in the regex list.
  2. The reducer therefore returns only candidates for that path: the auth route (with headers) and any fallback that shares the same path.
  3. The auth route is tried first but does not match (e.g. missing X-API-Key), so it is skipped
  4. The other candidates in the reduced set are only routes that match that first URI (same path string). A more specific route (e.g. /api/v1/login with higher regex_priority, no headers) is never in the candidate set because its path string is different from hits.uri, so it is never considered.

So the bug is: the router matches the auth route's path first, then the reducer restricts to that single URI; auth is tried and skipped, and the only remaining candidates are those for that first URI (e.g. fallback). The higher-priority route is skipped because the reducer only considers routes for the first matching path.

Example

  • Auth route: path /api(/.*), regex_priority = 0, requires headers (e.g. X-API-Key). Its path is the first to match the request.
  • Login route: path /api/v1/login, regex_priority = 200, no headers.
  • Fallback route: path /api(/.*), no headers.

Request: GET /api/v1/login (no auth header). Auth route's path matches first → hits.uri = "/api(/.*)" → reducer returns only auth + fallback → auth fails on headers → fallback wins. Login route is never in the candidate set.

Expected Behavior

When several path patterns match the request, the reducer should consider all of them (union of routes for each matching path), then sort by regex_priority (desc) and created_at (asc).

That way the auth route is still tried first (and skipped if headers don't match), but the login route is also in the candidate set and wins over the fallback because of higher regex_priority.

Steps To Reproduce

No response

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions