How can a router treat a trailing slash or a case difference between the request path and a registered template?
answer
- same resource, two spellings
- strict, lenient, or canonicalising
- the path is case-sensitive by spec
- extra URLs mean extra keys
- a redirect can drop the body
basics
~20 sThree policies are common: strict matching treats the spellings as different paths and misses, lenient matching sends both to one handler, and canonicalising redirects to the preferred form. Path segments are case-sensitive by specification, so ignoring case is an opt-in.
solid answer
~40 s`/orders` and `/orders/`, like `/Orders` and `/orders`, are genuinely different paths — only the scheme and host are case-insensitive, and only an empty path equals `/` at the root. Any equivalence is therefore router **policy**, and there are three: strict (a near-miss is a path miss), lenient (both spellings reach the same handler), and canonicalising (a redirect to the preferred spelling). Strict is predictable but unforgiving. Lenient gives one resource several live URLs, which multiplies cache keys, rate-limit counters, metrics labels and path-prefix access rules. Canonicalising costs a round trip, and unless the redirect status preserves the method, some clients replay a write as a plain read and drop its body. Pick one and register every template the same way.
go deeper
Recall that a trailing slash and a change of case make a different path, and that any leniency is a setting your framework applies rather than a rule of the web.
Explain the three policies and what each costs at request time, and be able to say which one your service uses and where that is configured.
Reason about what sits downstream of the router: cache keys, access rules, rate limiting, metrics cardinality, and the redirect that can turn a write into a read.
Set one canonical path form for the fleet and make every layer compare it the same way; divergence between the gateway's comparison and the router's is where the interesting failures live.
## Two paths that look the same to a human `/orders` and `/orders/` are different paths. `/Orders` and `/orders` are different paths. Nothing in the URI rules makes them equivalent: the path is compared as written, and only the scheme and host are case-insensitive. (The one exception is at the root, where an empty path and `/` mean the same thing.) So a router that treats any of these spellings as interchangeable is applying a **policy**, not obeying the specification — and the policy is worth choosing deliberately. ## The three policies a router can apply | Policy | Behaviour on a near-miss | Cost | Main risk | |---|---|---|---| | Strict | the paths differ, so it is a path miss | none | avoidable 404s from a stray slash or capital | | Lenient | both spellings reach the same handler | none at request time | one resource with several live URLs | | Canonicalising | a redirect to the preferred spelling | an extra round trip | the method and body of the original request | Some routers let the policy be set per route or per group; others apply one rule globally; a few apply it only to the trailing slash and never to case. What matters is that the service picks one and applies it uniformly, because the three are indistinguishable in a happy-path test and obviously different in production. ## Trailing slash in detail - **Strict matching** is the most predictable and the least forgiving: a client that appends a slash gets a path miss even though the resource plainly exists. - **Lenient matching** is convenient, but it means the same resource answers on two URLs. Anything keyed by the URL string — caches, rate-limit counters, metrics labels, path-prefix access rules — now has two keys where you designed one. - **Canonicalising** keeps one live URL and teaches clients the right form, at the price of a redirect. That redirect is cheap for a read and dangerous for a write: it costs a second round trip, and unless the redirect status is one that preserves the method, some clients will replay the request as a plain read and drop the body. Redirecting a non-read request is therefore something to do knowingly, or to avoid by accepting both spellings for those routes. Whichever policy applies, the templates in the codebase should be written one way. Mixing `/orders` and `/orders/` in registrations and relying on the router to reconcile them makes the route table unreadable. ## Case in detail Case-insensitive path matching looks like a kindness to users typing URLs, and it has a longer tail than the trailing slash: - **Duplicate identities.** Every mixed-case spelling is a distinct string to a cache, a log aggregator, a metrics label and an analytics report. Cardinality grows and hit rates fall. - **Prefix rules drift out of alignment.** If an access rule, a gateway rule or a proxy configuration matches a prefix case-sensitively while the router matches case-insensitively, a differently-cased request can be routed to a protected handler without being caught by the rule in front of it. The safe arrangement is for every layer to apply the same comparison, and the simplest way to get that is to keep all of them case-sensitive. - **Captures are unaffected.** A case-insensitive match still hands the handler the text the client sent, in the client's own case — so any comparison done inside the handler still has to decide about case for itself. The path is also where normalisation ordering matters: whether percent-escapes are decoded before or after the path is split into segments changes what the segments are, and whether dot segments are resolved or rejected changes which template a crafted path can reach. These are matching decisions, not cosmetic ones. ## A workable default 1. Match paths **case-sensitively** and register every template in one case, usually lowercase. 2. Pick **one** trailing-slash form for the whole service and write every template that way. 3. Choose strict or canonicalising for the other form, and if canonicalising, confine the redirect to safe read requests. 4. Make the choice explicit in configuration rather than inherited from a default, and cover it with a test per policy: the canonical form matches, the near-miss behaves as the policy says. ## What an interviewer is listening for That you know the two spellings are genuinely different paths; that you can name the three policies and what each costs; and that you connect lenient or redirecting behaviour to the things downstream of the router — the cache key, the access rule, the request body — rather than treating it as a convenience switch.
- Why is redirecting to the canonical form risky for a write request?It costs a second round trip, and unless the redirect status is one that preserves the method, some clients follow it as a plain read and discard the body — so the write silently never happens. Confining canonicalising redirects to safe reads, and accepting both spellings for writes, avoids the class entirely.
- Beyond duplicate URLs, where does case-insensitive matching create risk?Wherever another layer compares the path case-sensitively. A gateway or access rule matching a prefix exactly, while the router ignores case, lets a differently-cased path reach a handler the rule was meant to guard. Caches, rate-limit counters and metrics labels also split across spellings.
- Does normalisation order matter to matching?Yes. Whether percent-escapes are decoded before or after the path is split into segments changes what the segments are, and whether dot segments are resolved or rejected changes which templates a crafted path can reach. These are matching decisions with security consequences, not cosmetic ones.
saying these in an interview costs you the question
- Says URL paths are case-insensitive, like host names
- Believes the specification makes /orders and /orders/ equivalent
- Redirects a write to the canonical form and expects the body to survive
- Enables case-insensitive matching without checking prefix-based rules
- Mixes both slash forms in registrations and relies on the router