URL properties deep dive
You’ve seen how to create a URL object and read a few of its properties. But the URL object isn’t just for reading. Most of its properties are read-write, meaning you can change any part of a URL by assigning a new value. Let’s go through all of them and pay special attention to the ones that trip people up.
Every property at a glance
Let’s start with a URL that uses every part, including some you don’t see very often:
const url = new URL("https://user:[email protected]:8080/path?q=1#top"); This URL contains a username (user) and password (pass) embedded right into the string. You won’t see this often in modern web development, but it’s part of the URL specification.
Here are all the properties you can access on this object:
url.href; // "https://user:[email protected]:8080/path?q=1#top"
url.protocol; // "https:"
url.username; // "user"
url.password; // "pass"
url.host; // "example.com:8080" ← hostname + port
url.hostname; // "example.com" ← just the domain
url.port; // "8080"
url.pathname; // "/path"
url.search; // "?q=1"
url.hash; // "#top"
url.origin; // "https://example.com:8080" ← READ-ONLY!
url.searchParams; // URLSearchParams object ← (next lesson!) That’s a lot of properties. You won’t use all of them equally. pathname, search, hostname, and searchParams are the ones you’ll reach for most often in backend code. But knowing the full list helps when you run into edge cases.
Modifying URLs safely
Here’s the part that makes URL objects so useful. You can assign to most of these properties and the URL updates itself correctly:
const url = new URL("https://example.com/old-page");
url.pathname = "/new-page";
console.log(url.href);
// "https://example.com/new-page"
url.protocol = "http:";
console.log(url.href);
// "http://example.com/new-page"
url.hash = "section-3";
console.log(url.href);
// "http://example.com/new-page#section-3"
url.port = "3000";
console.log(url.href);
// "http://example.com:3000/new-page#section-3" Each time we assign to a property, the URL object keeps everything consistent. You don’t have to worry about accidentally breaking the URL format. It handles the colons, slashes, and question marks for you.
What do you think would happen if you set url.pathname = "new-page" without the leading slash? Try it. The URL object adds the slash for you automatically. It’s designed to keep things valid.
The confusing trio: host vs hostname vs origin
These three are easy to mix up, and they come up constantly. Here’s the definitive guide:
const url = new URL("https://example.com:8080/page");
url.hostname; // "example.com" ← just the domain name
url.host; // "example.com:8080" ← domain name + port
url.origin; // "https://example.com:8080" ← protocol + domain + port Think of it as layers, where each one adds more:
hostname → example.com
host → example.com:8080 (hostname + port)
origin → https://example.com:8080 (protocol + host) hostname is the smallest piece: just the domain. host adds the port. origin adds the protocol on top of that.
Default ports are hidden
When the port is the default for the protocol (443 for HTTPS, 80 for HTTP), something subtle happens. The port property returns an empty string:
const url = new URL("https://example.com");
url.port; // "" ← port 443 is the default, so it's hidden
url.host; // "example.com" ← no ":443" shown
url.origin; // "https://example.com"
const url2 = new URL("https://example.com:9999");
url2.port; // "9999" ← non-default, so it shows
url2.host; // "example.com:9999" This catches people off guard. If you check url.port expecting to see "443", you’ll get an empty string instead. The port is still 443, but the URL object hides it because it’s implied by the protocol.
Hostname is always lowercased
The URL specification says domain names are case-insensitive. The URL constructor normalizes this for you:
const url = new URL("https://EXAMPLE.COM/Path");
url.hostname; // "example.com" ← lowercased automatically
url.pathname; // "/Path" ← pathname keeps its case! This is important to notice: hostname is lowercased, but pathname is NOT. The paths /Users and /users are considered different resources on a server. The URL object respects that distinction.
search vs searchParams
These two are related but different, and understanding the distinction now will save you confusion later:
const url = new URL("https://example.com/page?color=red&size=10");
url.search; // "?color=red&size=10"
url.searchParams; // URLSearchParams { 'color' => 'red', 'size' => '10' } search is the raw query string as a plain string, starting with ?. It’s just text.
searchParams is a URLSearchParams object with helpful methods like .get(), .set(), and .delete(). It’s a structured, interactive representation of the same data.
You can use either one depending on what you need. But searchParams is almost always the better choice because it gives you methods to read and modify individual parameters without parsing the string yourself.
We’ll explore searchParams deeply in the next lesson.
Which URL property is read-only (you can't change it by assigning a value)?