Batu Lab NotesPractical developer guides

Keep a relative redirect target unresolved

By Batu · English technical notes

Also published in our primary archive.

A redirect target can be relative to the URL that produced it. When code only needs to preserve, log, validate, or pass that target to a later resolver, parsing it must not accidentally turn it into an absolute URL. This example feeds ../v2/items?source=old to urllib.parse.urlsplit(). Its scheme and network location are empty, while its path and query remain separate. Calling geturl() reconstructs the original relative reference exactly.

Python’s urllib.parse documentation describes URL splitting into components. urlsplit() is useful here because it examines the reference without requiring a base URL. In contrast, urljoin(base, target) is the operation that resolves a relative reference, and its result depends on the selected base. Deferring that operation prevents an earlier layer from choosing an origin or path context it does not own.

The assertions establish the expected parse for this particular input, including the leading .. segment. They do not establish that the path is safe to fetch, suitable for a filesystem, or allowed by a redirect policy. Before eventually resolving and following a redirect, an application may still need to restrict schemes, origins, path traversal semantics, or redirect depth according to its own requirements. This code uses long-standing standard-library APIs and needs no newer Python-version feature. AI assistance was used to draft this article.

from urllib.parse import urlsplit

target = "../v2/items?source=old"
parts = urlsplit(target)

assert parts.scheme == ""
assert parts.netloc == ""
assert parts.path == "../v2/items"
assert parts.query == "source=old"
assert parts.geturl() == target
print(parts.geturl())
../v2/items?source=old