Resolve a relative URL against a base URL
Turn a relative link, path, query, fragment, or protocol-relative reference into the exact absolute URL a browser would use.
Run — free
Provide an absolute base URL and the reference found in a document, feed, stylesheet, manifest, or API response. The resolver applies standard URL behavior to path segments, trailing slashes, queries, fragments, ports, and origin changes, returning one normalized absolute URL without requesting the destination or inspecting any remote content. It is fast and deterministic.
Start with the complete URL that supplies context
A relative reference has meaning only in relation to another URL. Enter that context in base_url as a complete absolute URL, including its scheme and host when the scheme uses a host. The path matters as much as the domain. For example, a base ending in <code>/docs/page.html</code> treats a plain reference such as <code>image.png</code> as a sibling of the page, while a base ending in <code>/docs/</code> treats it as a child of that directory. The resolver does not guess whether a path represents a file or folder; it follows the syntax, especially the final slash. It accepts standard absolute URL forms understood by the browser URL implementation. A value such as <code>/docs/page.html</code> is not sufficient as a base because it lacks an absolute scheme and origin context. Invalid or relative bases produce an input error instead of a plausible-looking answer. This strictness makes mistakes visible before resolved links enter a crawler, import job, redirect rule, or stored dataset.
Understand how each kind of reference changes the base
The relative_reference field can contain more than a simple filename. A leading slash replaces the base path while retaining the base scheme and authority. Two leading slashes create a network-path reference, allowing the host to change while inheriting the scheme. Dot segments such as <code>../</code> move up path levels and <code>./</code> stays at the current level; normalization removes those navigation segments from the final URL. A query-only reference beginning with <code>?</code> keeps the path and replaces the query. A fragment-only reference beginning with <code>#</code> keeps the existing path and query while changing the fragment. A fully absolute reference replaces the base altogether, as standard resolution requires. Empty references are rejected by this capability so accidental missing form values are not mistaken for deliberate reuse of the base. Percent-encoded characters remain encoded according to URL serialization rules, while host names, default ports, and other components may be normalized into the canonical form produced by the standard URL parser.
Use the serialized result without triggering a network request
The result contains absolute_url, the normalized serialized URL after resolution. No DNS lookup, HTTP request, redirect follow, page download, or availability check occurs. That separation is useful when you need deterministic transformation rather than a live-site test: the same two inputs always return the same output, even when the host does not exist or the resource is private. Use the result to normalize links extracted from HTML, prepare crawl queues, expand references in feeds, validate fixtures, build sitemap entries, or compare URLs consistently before deduplication. Remember that syntactic resolution does not prove that the destination is safe, reachable, public, or authorized. If untrusted users supply the values, apply your application’s scheme, host, and network-access policy before fetching the returned URL. The API and browser runner use the same pure algorithm, so interactive checks and automated calls agree. Each API request costs $0.002; browser execution on this page is free and keeps the calculation local.
What you can do with it
Expand links extracted from a page
Convert relative href and src values into absolute URLs using the document URL that supplied them.
Normalize crawler queue entries
Resolve dot segments, root-relative paths, query references, and fragments before URLs enter a crawl or deduplication pipeline.
Test routing and URL fixtures
Generate deterministic expected URLs for application tests without contacting hosts or depending on network state.
FAQ
What does a trailing slash on the base URL change?
It determines whether the final path segment is treated as a directory. Without the slash, a plain relative path replaces that final segment; with it, the path is appended inside the directory.
Can the relative reference be an absolute URL?
Yes. Standard resolution returns that absolute URL independently of the base, with normal URL serialization applied.
Does this check whether the resolved URL exists?
No. It performs no network request, DNS lookup, redirect check, or availability test.
Are query-only and fragment-only references supported?
Yes. A reference beginning with ? replaces the query, while one beginning with # changes the fragment according to standard URL rules.
What happens when the base URL is relative?
The request fails with an invalid input error because a relative base does not provide enough context to produce an absolute URL.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/web/absolute-url-resolve \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"base_url":"https://example.com/docs/guides/start.html","relative_reference":"../images/logo.svg?theme=dark#mark"}'const res = await fetch("https://api.kit.forhosting.com/web/absolute-url-resolve", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"base_url": "https://example.com/docs/guides/start.html",
"relative_reference": "../images/logo.svg?theme=dark#mark"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/web/absolute-url-resolve",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"base_url": "https://example.com/docs/guides/start.html",
"relative_reference": "../images/logo.svg?theme=dark#mark"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/web/absolute-url-resolve", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"base_url":"https://example.com/docs/guides/start.html","relative_reference":"../images/logo.svg?theme=dark#mark"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"base_url":"https://example.com/docs/guides/start.html","relative_reference":"../images/logo.svg?theme=dark#mark"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/web/absolute-url-resolve", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"base_url": "https://example.com/docs/guides/start.html",
"relative_reference": "../images/logo.svg?theme=dark#mark"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "web.absolute_url_resolve",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
timeout_sec | 30 |
max_crawl_pages | 25 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |