The default HttpClient follows redirects unconditionally up to a
configurable hop count and silently strips state-changing methods
(POST → GET on 301/302/303, per the spec). That covers the
80% case but leaves three sharp edges for callers who care:
- Cross-origin redirects. A 302 from
api.example.com to
evil.com is a textbook open-redirect leak — every
request your client sends to api.example.com (including
the Authorization header, cookies, custom auth tokens) will
silently follow to evil.com if you don't gate on origin.
:class:RedirectPolicy.same_origin_only says "follow same-origin
redirects, error on cross-origin". :class:.deny says "never
follow; surface the 3xx to the caller".
- Authorization-header forwarding. Even when a cross-origin
redirect is intentional (image CDN with separate auth domain,
for example), the Authorization header should not be forwarded
to the new host by default — that's the requests-library
default since 2014 and curl's --location-trusted behaviour
inverts. :class:
.forward_auth_cross_origin (default False)
controls this.
- 307 / 308 method preservation. RFC 9110 §15.4 mandates
that 307 / 308 redirects preserve the original method + body
(whereas 301 / 302 / 303 historically degrade POST → GET).
The default policy honours this; the :func:
.decide helper
is the single source of truth.
Public surface:
- :class:
RedirectPolicy — the policy struct (max hops + cross-
origin gating + auth-forwarding gating).
- :class:
RedirectAction — the three-way decision: Follow,
Stop (cap hit / DENY rule), or Reject (cross-origin
while in SAME_ORIGIN mode).
- :class:
RedirectDecision — the decision plus its derived
next-request shape (method + whether to forward the Authorization
header).
The decision-helper :meth:RedirectPolicy.decide is pure (no
network), so the :class:HttpClient can be retrofitted onto
it as a follow-up without touching the policy logic. It
also makes the policy unit-testable in isolation, which is the
entire point — each cross-origin / auth-forwarding rule has an
explicit assertion in :mod:tests.test_redirect_policy.
Example:
from flare.http import (
HttpClient, RedirectPolicy, RedirectMode,
)
# Restrict redirects to the same origin (scheme + host + port);
# refuse to forward Authorization across origins.
var pol = RedirectPolicy.same_origin_only(max_redirects=5)
# Equivalent open-redirect-tolerant configuration (the legacy
# default, byte-for-byte):
var pol_legacy = RedirectPolicy.follow_all(max_redirects=10)