Toolmingo
Guides6 min read

How to Parse a URL in JavaScript, Python, Go, and PHP

Learn how to break a URL into its components — scheme, host, path, query string, and fragment — with built-in parsers in JS, Python, Go, and PHP.

Every URL is a structured string with well-defined parts. Parsing a URL means splitting it into those parts so you can read, modify, or reconstruct it safely — without fragile string splits or regex hacks.

URL anatomy

Take this example URL:

https://www.example.com:8080/search?q=hello+world&lang=en#results
Component Value Name
https protocol scheme
www.example.com hostname host
8080 port port
/search path pathname
q=hello+world&lang=en query string search
results hash target fragment / hash

The ? and # are delimiters — they don't belong to the query string or fragment themselves.

JavaScript: URL and URLSearchParams

The browser-native URL constructor is the modern standard. It works in all current browsers and in Node.js (since v10).

const url = new URL("https://www.example.com:8080/search?q=hello+world&lang=en#results");

console.log(url.protocol);  // "https:"
console.log(url.hostname);  // "www.example.com"
console.log(url.port);      // "8080"
console.log(url.pathname);  // "/search"
console.log(url.search);    // "?q=hello+world&lang=en"
console.log(url.hash);      // "#results"
console.log(url.origin);    // "https://www.example.com:8080"

Parsing query parameters with URLSearchParams:

const params = new URLSearchParams(url.search);

params.get("q");     // "hello world"  (decoded automatically)
params.get("lang");  // "en"

// Iterate all params
for (const [key, value] of params) {
  console.log(key, value);
}

// Check existence
params.has("q");    // true
params.has("page"); // false

URLSearchParams handles decoding automatically — hello+world becomes "hello world" and %C3%A9 becomes "é".

Modifying and rebuilding a URL:

const url = new URL("https://example.com/search?q=old");
url.searchParams.set("q", "new query");
url.searchParams.append("page", "2");
url.pathname = "/results";

console.log(url.toString());
// "https://example.com/results?q=new+query&page=2"

Relative URLs require a base:

const url = new URL("/about", "https://example.com");
// "https://example.com/about"

Error handlingnew URL() throws a TypeError for invalid URLs:

function tryParseUrl(raw) {
  try {
    return new URL(raw);
  } catch {
    return null;
  }
}

Python: urllib.parse

Python's standard library urllib.parse covers all URL parsing needs.

from urllib.parse import urlparse, parse_qs, parse_qsl, urlencode, urlunparse

raw = "https://www.example.com:8080/search?q=hello+world&lang=en#results"
parts = urlparse(raw)

print(parts.scheme)    # "https"
print(parts.netloc)    # "www.example.com:8080"
print(parts.hostname)  # "www.example.com"
print(parts.port)      # 8080  (int, not string)
print(parts.path)      # "/search"
print(parts.query)     # "q=hello+world&lang=en"
print(parts.fragment)  # "results"

Parsing query parameters:

from urllib.parse import parse_qs, parse_qsl

# parse_qs: values are lists (handles repeated keys)
params = parse_qs(parts.query)
print(params)  # {'q': ['hello world'], 'lang': ['en']}
print(params["q"][0])  # "hello world"

# parse_qsl: list of (key, value) tuples — preserves order
for key, value in parse_qsl(parts.query):
    print(key, value)

parse_qs always returns lists because a key can appear multiple times in a query string (e.g. ?tag=a&tag=b).

Rebuilding a URL:

from urllib.parse import urlunparse

new_query = urlencode({"q": "new query", "page": "2"})
rebuilt = urlunparse((
    parts.scheme,
    parts.netloc,
    parts.path,
    "",          # params (rare; used for path params)
    new_query,
    ""           # fragment
))
print(rebuilt)
# "https://www.example.com:8080/search?q=new+query&page=2"

Resolving relative URLs:

from urllib.parse import urljoin

base = "https://example.com/docs/v1/"
print(urljoin(base, "guide"))          # "https://example.com/docs/v1/guide"
print(urljoin(base, "/about"))         # "https://example.com/about"
print(urljoin(base, "https://other.com")) # "https://other.com"

Go: net/url

Go's net/url package parses URLs into a *url.URL struct.

package main

import (
    "fmt"
    "net/url"
)

func main() {
    raw := "https://www.example.com:8080/search?q=hello+world&lang=en#results"
    u, err := url.Parse(raw)
    if err != nil {
        panic(err)
    }

    fmt.Println(u.Scheme)   // "https"
    fmt.Println(u.Host)     // "www.example.com:8080"
    fmt.Println(u.Hostname()) // "www.example.com"
    fmt.Println(u.Port())   // "8080"
    fmt.Println(u.Path)     // "/search"
    fmt.Println(u.RawQuery) // "q=hello+world&lang=en"
    fmt.Println(u.Fragment) // "results"
}

Parsing query parameters:

params, _ := url.ParseQuery(u.RawQuery)
// or: params := u.Query()  (same result)

fmt.Println(params.Get("q"))    // "hello world"
fmt.Println(params.Get("lang")) // "en"

// Repeated keys
for _, v := range params["tag"] {
    fmt.Println(v)
}

url.Values is map[string][]string — same as Python's parse_qs, values are slices.

Modifying and rebuilding:

params := u.Query()
params.Set("q", "new query")
params.Add("page", "2")
u.RawQuery = params.Encode()
u.Path = "/results"

fmt.Println(u.String())
// "https://www.example.com:8080/results?lang=en&page=2&q=new+query"

Note: url.Parse is lenient and rarely returns an error. Use url.ParseRequestURI when you need strict validation (rejects relative URLs and bare paths).

PHP: parse_url

PHP's parse_url returns an associative array. Keys are absent (not null) when a component doesn't exist.

<?php
$raw = "https://www.example.com:8080/search?q=hello+world&lang=en#results";
$parts = parse_url($raw);

echo $parts['scheme'];   // "https"
echo $parts['host'];     // "www.example.com"
echo $parts['port'];     // 8080  (int)
echo $parts['path'];     // "/search"
echo $parts['query'];    // "q=hello+world&lang=en"
echo $parts['fragment']; // "results"

Parsing query parameters:

parse_str($parts['query'], $params);

echo $params['q'];    // "hello world"
echo $params['lang']; // "en"

// Repeated keys need array syntax in the query string: tag[]=a&tag[]=b

Rebuilding a URL (PHP has no http_build_url by default, build it manually):

function buildUrl(array $parts): string {
    $url = '';
    if (isset($parts['scheme']))   $url .= $parts['scheme'] . '://';
    if (isset($parts['host']))     $url .= $parts['host'];
    if (isset($parts['port']))     $url .= ':' . $parts['port'];
    if (isset($parts['path']))     $url .= $parts['path'];
    if (isset($parts['query']))    $url .= '?' . $parts['query'];
    if (isset($parts['fragment'])) $url .= '#' . $parts['fragment'];
    return $url;
}

$parts['query'] = http_build_query(['q' => 'new query', 'page' => 2]);
echo buildUrl($parts);
// "https://www.example.com:8080/search?q=new+query&page=2"

Quick reference

Task JavaScript Python Go PHP
Parse URL new URL(raw) urlparse(raw) url.Parse(raw) parse_url($raw)
Get hostname url.hostname p.hostname u.Hostname() $p['host']
Get path url.pathname p.path u.Path $p['path']
Get query string url.search p.query u.RawQuery $p['query']
Parse query params new URLSearchParams(url.search) parse_qs(p.query) u.Query() parse_str($q, $p)
Get one param params.get("key") params["key"][0] params.Get("key") $params['key']
Rebuild URL url.toString() urlunparse(parts) u.String() manual
Resolve relative new URL(rel, base) urljoin(base, rel) base.ResolveReference(ref)

Common mistakes

Splitting on ? or # with string methods. url.split("?")[1] breaks as soon as the URL has a fragment, no port, or encoded ? in a parameter value. Use a proper parser — it handles all edge cases.

Forgetting that query parameter values are decoded. URLSearchParams.get("q") returns "hello world", not "hello+world". If you compare against the raw string, the match fails.

PHP's repeated key syntax. PHP's parse_str silently drops duplicate keys unless they use bracket notation (tag[]=a&tag[]=b). If you receive query strings from an external source, check for dropped data.

Go's url.Parse accepting invalid URLs. url.Parse("://broken") may not error — use url.ParseRequestURI or validate u.Host != "" explicitly.

Port is a string in some languages. In JavaScript, url.port is a string ("8080", or "" if it's the default). In Go, u.Port() is also a string. Only PHP and Python return a numeric port.

parse_url returns false on malformed input. PHP's parse_url("not a url at all") may return false instead of an array. Always check the return value before accessing keys.

FAQ

Q: Does new URL() work in Node.js? Yes. URL is globally available in Node.js since v10.0 (without importing anything). In older Node.js (v7–v9) you needed require('url').URL.

Q: How do I parse a URL from a browser's address bar? window.location is already a parsed URL object with .hostname, .pathname, .search, .hash, etc. You don't need new URL() for the current page.

Q: How do I handle query parameters that appear multiple times? All languages treat repeated keys as arrays/lists: params.getAll("tag") in JS, params["tag"] (a list) in Python, params["tag"] (a slice) in Go. PHP requires bracket notation (tag[]=a&tag[]=b) for arrays.

Q: How do I check if a URL is absolute or relative? In JS, new URL(str) throws for relative URLs — use a try/catch. In Python, urlparse returns a result with an empty scheme for relative paths. In Go, url.Parse succeeds for both; check u.IsAbs(). In PHP, check isset(parse_url($url)['host']).

Q: How do I URL-encode a value before inserting it into a URL? Use encodeURIComponent (JS), urllib.parse.quote (Python), url.QueryEscape (Go), or rawurlencode (PHP). For a full guide, see How to URL Encode a String.

Q: Can I parse a URL without a library? Technically yes, but you shouldn't. URL syntax has many edge cases: IPv6 addresses in brackets ([::1]), encoded characters in hostnames, default ports that should be omitted, path normalization (. and ..). Built-in parsers handle all of them correctly.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools