URL and Encoding
- A URL consists of a scheme, a host with an optional port, a path,
a query (
?…) and a fragment (#…). The fragment is not sent to the server. - Characters that are not allowed or have a special meaning (
&,=,#,?, spaces, non-ASCII) are percent-encoded: each UTF-8 byte becomes%XX.encodeURIComponentencodes one part of a URL;encodeURIa whole URL, so it leaves&,=,?,/and#as they are. - Build and parse URLs with
new URL(path, base)andURLSearchParams, not string concatenation.
Details
Parts of a URL
For https://user:pw@example.com:8080/a/b?q=1#top:
| Property | Value | Notes |
|---|---|---|
protocol | https: | With the colon |
username | user | Credentials in URLs are deprecated |
host | example.com:8080 | hostname + port |
hostname | example.com | |
port | 8080 | Empty string for the default port (443 for https) |
pathname | /a/b | |
search | ?q=1 | searchParams is a live URLSearchParams of it |
hash | #top | Stays in the browser |
origin | https://example.com:8080 | Scheme + host + port; read-only |
The origin is what the Same-Origin Policy compares. See CORS and the Same-Origin Policy.
new URL and URLSearchParams
new URL(input, base)resolves a relative URL against a base, as the browser does for links:new URL('../img/a.png', 'https://ex.com/a/b/page.html')→https://ex.com/a/img/a.png. It also normalizes: lowercases the scheme and host, drops the default port, resolves.and...- It throws
TypeError: Invalid URLfor invalid input, for example a relative URL without a base.URL.canParse(input, base)returns a boolean;URL.parse(input, base)returns theURLornullinstead of throwing. - The properties are writable:
url.pathname = …,url.hash = …;url.hrefgives the result. URLSearchParamsreads and builds the query:get,getAll(repeated keys),set,append,delete,has,sort, iteration. It accepts a string, an object or an array of pairs, and is accepted byfetchas a form body.URLPatternmatches URLs against patterns like/users/:idand returns the parameters: a built-in alternative to route-matching libraries.
Encoding Functions
| Function | Leaves as is | Space | Use for |
|---|---|---|---|
encodeURIComponent | Letters, digits, - _ . ! ~ * ' ( ) | %20 | One value: a path segment, a param |
encodeURI | The above plus ; , / ? : @ & = + $ # | %20 | A whole URL with spaces or non-ASCII chars |
URLSearchParams | Letters, digits, * - . _ | + | Query strings and form bodies |
- In a query string,
+means a space forURLSearchParamsand form parsers, but not fordecodeURIComponent, which leaves+as it is. decodeURIComponentthrowsURIError: URI malformedfor an invalid sequence such as%E0%A4%A; wrap user input intry.- Double encoding: encoding an already encoded value turns
%20into%2520. Encode once, at the place where the URL is built. escape/unescapeare deprecated: they do not use UTF-8 (€→%u20AC).- Internationalized domain names are converted to punycode:
new URL('https://пример.рф').hostisxn--e1afmkfd.xn--p1ai. - Cookie values are encoded with the same
encodeURIComponent. See Browser Storage.
Security
- Validate the protocol of a user-supplied URL before putting it into
hreforsrc:javascript:URLs run code. See Cross-Site Scripting (XSS). - Check the origin of a redirect target (
?next=…), not a prefix of the string:https://example.com.evil.comstarts withhttps://example.com.
Support: URL, URLSearchParams and URL.canParse() are Baseline;
URL.parse() and URLPattern became Baseline in 2024 and 2025.
Example
A query built by concatenation breaks on &:
const query = 'Tom & Jerry';
const bad = `/search?q=${query}&page=2`; // q is 'Tom ', and ' Jerry' becomes a key
new URLSearchParams(bad.split('?')[1]).get('q'); // 'Tom '
const url = new URL('/search', 'https://example.com');
url.searchParams.set('q', query);
url.searchParams.set('page', 2);
url.href; // 'https://example.com/search?q=Tom+%26+Jerry&page=2'
url.searchParams.get('q'); // 'Tom & Jerry'
Follow-up Questions
- Why is the hash not sent to the server? It addresses a place inside the document, so the browser handles it; that is why hash routing needs no server configuration. See History API.
- How do you pass an array in a query string? There is no standard: repeated
keys
?id=1&id=2(built withappend, read withgetAll), orid[]=or comma-separated values, as the server expects. - What does
new URL('api', 'https://ex.com/a/b')return?https://ex.com/a/api: without a trailing slash,bis a file and is replaced. - How do you read the current page's parameters?
new URL(location.href).searchParamsornew URLSearchParams(location.search). - Is there a length limit? The standard sets none, but servers and CDNs often
reject URLs over about 8 KB; large data goes in a
POSTbody.
Common Mistakes
- Building URLs with template strings and forgetting to encode user input.
- Using
encodeURIfor a parameter value. - Decoding
+withdecodeURIComponentand getting+instead of a space. - Encoding a value twice, once by hand and once by
URLSearchParams. - Checking a URL with
startsWithor a regular expression instead of parsing it and comparingorigin.