Redirect Handling for Browser Agents: Why requested_url and redirected Exist

7

min read

Product

Redirect handling for browser agents is one of those problems that looks trivial until an agent silently acts on the wrong page. A human clicking a stale product link notices the redirect immediately — different photos, different price, maybe a different language entirely. An agent reading a JSON response has no such intuition. If nothing in that response says "this isn't the page you asked for," the agent will happily fill out a form, click a button, or make a decision based on content that has nothing to do with the URL it requested.

We hit this ourselves. Here's the bug, the fix, and why we think this is a problem every browser-agent tool has to solve honestly rather than paper over.

The failure mode: a page swap with no signal

The specific case that surfaced this was a Rothy's product page. Manifest was asked to scan a product URL — a pair of flats — and Playwright, following a server-side 302, landed on rothys.com/en-pt, the site's Portuguese-locale homepage. This happens for boring, common reasons: geo-detection on the requesting IP, a discontinued SKU, a moved page, an A/B redirect. It's not a Manifest bug in the sense of "broken code" — it's just what browsers do when a server tells them to go somewhere else.

The actual bug was upstream of that: get_action_manifest stamped the response's url field with the URL that was requested, unconditionally — even when the browser had actually landed somewhere completely different. So a caller would ask for the flats page, get back a manifest whose url field said "flats page," and whose actions and current_page_state described a homepage with a country switcher and a newsletter signup. Nothing in the payload told you the two didn't match. An agent trying to add the flats to a cart would find no cart-related actions at all, with no way to know why — because as far as the response claimed, it was looking at the right page.

That's the core issue with redirect handling for browser agents: a redirect isn't a failure a tool needs to prevent. It's information the tool needs to report. Silently normalizing the URL back to what was requested is worse than doing nothing, because it actively hides the mismatch instead of surfacing it.

The fix: requested_url and redirected

The fix separates "what was asked for" from "what was captured," as two explicit fields on every manifest response:


json


{
  "url": "https://rothys.com/en-pt",
  "requested_url": "https://rothys.com/products/womens-pointed-toe-flat-tan-woven",
  "redirected": true,
  "current_page_state": "Portuguese-locale homepage with country switcher",
  "actions": [ ... ],
  "navigation": [ ... ]
}

url now reflects the page Manifest actually captured — the page current_page_state and actions genuinely describe. requested_url preserves what the caller originally asked for. redirected is true whenever those two differ in a way that matters. A caller — human or agent — can check redirected before trusting the rest of the payload, instead of discovering the mismatch by getting confused about why an "Add to Cart" action never shows up.

The Python and JS/TS SDKs surface this the same way: manifest.requested_url and manifest.redirected on the response object, so an agent loop can gate on it directly:


python


manifest = client.get_action_manifest(url)

if manifest.redirected:
    # current_page_state and actions describe manifest.url,
    # not the url you requested — decide what to do about that
    # before acting on anything in this response.
    handle_unexpected_redirect(manifest.requested_url, manifest.url)

Why not just compare the two URLs as strings?

Because a naive string comparison creates false positives that would make the flag useless. A trailing slash, an added query parameter, or a URL fragment doesn't mean the page changed — and if redirected fired on every cosmetic difference, nobody would trust it. The comparison Manifest uses is host-plus-path only, so example.com/pricing and example.com/pricing/ or example.com/pricing?ref=hn are treated as the same page. redirected is reserved for genuine navigation elsewhere — a different path entirely, or a different host, which is what an actual server-side redirect produces.

That distinction matters for redirect handling for browser agents specifically, because the whole point of the flag is that an agent can trust it without cross-checking. A flag that cries wolf on query-string noise gets ignored; a flag that only fires on real page swaps gets acted on.

It generalizes: locale redirects are the same problem in disguise

The requested_url/redirected pattern turned out to be a special case of a broader issue. Our external collaborator Sergei ran into a variant on a SKIMS product page: he requested the scan with country="US", but the page server-side redirected to /en-de/, serving EUR pricing and German-locale copy — with nothing in the response indicating the page wasn't the US version he asked for. Same failure shape as the Rothy's bug, just triggered by geo-routing instead of a moved page.

The fix follows the same honesty principle rather than a different one. Manifest now accepts an optional country parameter that sets Accept-Language on the scan, and reports back requested_country, served_locale (detected from the URL's locale segment, e.g. en-DE), and locale_mismatch — true only when a country was requested, a served locale was actually detected, and the two disagree. If there's no locale signal in the URL at all, locale_mismatch stays false rather than guessing — an honest "unknown" instead of a false alarm, the same tolerance redirected already applies to cosmetic URL differences.

Worth being direct about the limits here: setting Accept-Language is a request to the server, not a guarantee. Some sites gate locale routing on IP address and ignore the header entirely, in which case locale_mismatch will correctly flag that the steering didn't work — Manifest reports the mismatch, it doesn't yet reroute around it with proxy-based IP steering.

What this means for redirect handling for browser agents in practice

None of this stops a redirect from happening — that's not the job. A CDN, a geo-router, or a marketing team's URL migration is going to 302 whenever it wants to, regardless of what's scanning the page. What Manifest changed is what happens after that: instead of silently relabeling the landed page with the URL you asked for, it tells you plainly that a swap occurred and hands you both URLs so you can decide what to do — retry with a different country, surface the mismatch to a human, or just skip the action and move on.

For an agent making autonomous decisions off a page it never rendered itself, that distinction — silently wrong versus honestly flagged — is the whole ballgame.

FAQ

What does redirected: true mean in a Manifest API response? It means the page Manifest actually captured (url) differs from the page originally requested (requested_url) after a server-side redirect — not a client-side link click, a genuine 3xx response the browser followed during navigation.

Why does an AI agent need to know a page redirected, instead of the tool just resolving it silently? Because the manifest's actions and current_page_state describe whatever page was actually captured, not the one requested. An agent acting on those fields without knowing a swap occurred can end up filling out the wrong form, missing an action that only exists on the intended page, or making a decision based on content for a different locale or product entirely.

Does Manifest prevent redirects or follow a specific one? No. Manifest doesn't intervene in navigation — it reports what happened. requested_url and redirected are an honesty layer on top of whatever the browser did, not a redirect policy.

Do trailing slashes or query parameters trigger redirected? No. The comparison is host-plus-path only, so cosmetic differences like a trailing slash, an added query string, or a URL fragment don't count as a redirect. redirected is reserved for a genuinely different path or host.

How is this different from the locale/geo fields (requested_country, served_locale, locale_mismatch)? Same honesty pattern, different dimension. requested_url/redirected catch a page-level swap; requested_country/served_locale/locale_mismatch catch a locale-level mismatch when a country was requested but the server routed you to a different locale anyway (with the same "no signal means unknown, not a false alarm" tolerance).

Where do I read these fields in code? Both the Python SDK and JS/TS SDK expose them directly on the manifest response — requested_url / redirected (and requested_country / served_locale / locale_mismatch when a country param was passed) — no separate call needed.

omfang logo

Follow us on social media

Contact us

Learn more about omfang