Why can one build-declared redirect table behave differently on a file host, a function platform and a long-running server?
answer
- the build declares, the deployment executes
- an adapter translates for each target
- static configs cap and simplify rules
- conditions on headers rarely survive translation
- assert status and Location on a preview
basics
~20 sThe build only emits the rules; the hosting target executes them. A static host's generated routing config, a function platform's request handler and an in-process router differ in expressiveness, ordering, rule limits and cost, so identical rules diverge.
solid answer
~50 sA build-declared table is a **manifest**, not behaviour. The build emits source patterns and destinations; an adapter translates them into whatever the target understands, and that engine applies them. A plain file host or CDN gets a generated routing config — fast and free, but usually limited to pattern matching, with a cap on rule count and no way to express a condition on a cookie or header. A function platform may hoist the table to its edge layer or may hand the request to your code, in which case every matching redirect costs an invocation. A long-running process runs the framework's own router with full expressiveness. On top of that, hosts normalise trailing slashes and case, and order rules against static-file lookups differently. The practical consequence: assert the rules against a deployed preview, not against the config file.
go deeper
Remember that redirect rules written in configuration are data emitted by the build; something at the deployment boundary has to read them and act on each request.
Explain the translation step and what each target can express, including rule caps, path-only matching on static hosts, and the per-request cost when a function handles the rule instead of the edge.
Show that you test the deployed artifact: request each source path without following it and assert the status and Location, and alert on rule counts approaching the target's cap.
Weigh portability against expressiveness when choosing a hosting target, and set the rule that redirect behaviour is re-verified whenever the deployment shape changes, since the source can stay untouched while the engine changes.
## A build-declared table is a declaration, not an implementation When redirects and rewrites are written in configuration, the build does not *perform* them. It emits a manifest of rules — source pattern, destination, permanent or temporary, any conditions — alongside the rest of the build output. Something at the deployment boundary then has to read that manifest and apply it to real requests. **Which "something" that is, is the whole answer**: the same table is executed by a different engine on every hosting target, and the engines are not equally capable. Most meta-frameworks handle this with an **adapter** layer: the build output is post-processed into the shape a particular target expects. That is why one project can be deployed three ways from one source and see three different behaviours from one unchanged rule list. ## The three shapes of target | Target | Who executes a rule | Expressiveness | Cost of a rule | |---|---|---|---| | File host or CDN serving static output | the host's own routing config, generated from the manifest at deploy time | lowest: pattern matching, sometimes wildcards; conditions on headers or cookies usually not available; rule counts are capped | effectively free, handled at the edge before any compute | | Function platform | the framework's request handler inside an invocation, unless the platform's edge layer handles the table first | full, since your code runs | one invocation per matching request when the table is not hoisted to the edge | | Long-running server process | the framework's own router in process | full | negligible per request, but no shared cache in front unless a CDN is added | The uncomfortable middle case is the function platform: a redirect that could have been answered at the edge for nothing sometimes costs a cold start and an invocation, simply because the table was not hoisted into the platform's routing layer. ## Where the same table diverges - **Rules are silently dropped.** Static host configurations cap the number of rules. A table that grows past the cap can deploy with the tail missing and no build failure. - **Conditions do not survive.** A rule matching on a cookie, a header or a query parameter cannot be expressed by most static host configurations, so it either fails to compile or is discarded. - **Ordering rules differ.** The framework's table is usually first-match-wins in declaration order; a host may apply its own specificity ordering, or evaluate its static-file lookup before consulting rules at all, so a rule shadowing an existing file behaves differently. - **Normalisation happens first.** Trailing slashes, case, and duplicate slashes are often normalised by the host or CDN before your table is consulted, so a rule written against the un-normalised path never matches. - **Cross-origin rewrites may be unavailable.** Proxying another origin under a path prefix requires a component willing to make an outbound request; a pure file host has none. - **Permanence is enforced by the client.** A rule declared permanent is remembered by browsers and shared caches regardless of target, so a rule you later delete keeps firing for returning visitors long after the deploy that removed it. ## How to work with it rather than against it 1. **Know the target before writing the table.** Decide early whether the deployment has compute in front of the content; that single fact determines what the table may contain. 2. **Keep rules simple and few.** Prefer a small number of prefix and wildcard rules over hundreds of one-to-one entries, and move genuinely data-driven redirects into a per-request lookup rather than generating a giant static list. 3. **Test the deployed artifact, not the config.** Assert against a deployed preview: for each source path, request it without following the redirect and check the status and `Location`. That is the only check that exercises the engine actually running the rules. 4. **Alert on the cap.** If the target has a rule limit, assert the rule count in the build so growth fails loudly rather than truncating quietly. 5. **Re-verify after changing targets.** Moving from a container to a function platform, or adding a CDN in front, changes the engine executing the table even though the source is untouched. The senior instinct here is to stop treating redirect configuration as application code that "just works" and start treating it as an artifact compiled for a specific runtime — one whose losses in translation are silent, and therefore need a test that runs against the deployment rather than the repository.
- What makes a rule that matches on a cookie hard to put in a build-declared table?It needs the request to be inspected, and a static host's routing config typically matches on the path alone. Such a rule either fails translation or is dropped, so it belongs in per-request code — accepting that it now runs compute on every matching request, and that the response must be marked as varying by that cookie if anything caches it.
- How would you stop a growing redirect table from truncating silently at deploy time?Assert the count in the build against the target's documented limit so growth fails the pipeline, and collapse one-to-one entries into prefix or wildcard rules where the shape allows. For genuinely large, data-driven mappings, replace the table with a per-request lookup backed by a cache instead of generating thousands of static rules.
saying these in an interview costs you the question
- Believes the build itself performs the redirects
- Assumes a rule list behaves identically on every host
- Thinks header or cookie conditions always translate to static config
- Never verifies rules against a real deployment
- Ignores host-side trailing-slash and case normalisation
- Expects removing a permanent rule to stop it firing immediately