url.format()
Serializes a WHATWG URL to a string, optionally dropping the fragment, query, or credentials.
url.format(URL[, options])- Added in Node.js v7.6.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$URL | URL | A WHATWG URL object. |
$optionsoptional | Object | Options controlling which components appear in the output. |
$options.authoptional | boolean= true | true if the serialized URL string should include the username and password, false otherwise. |
$options.fragmentoptional | boolean= true | true if the serialized URL string should include the fragment, false otherwise. |
$options.searchoptional | boolean= true | true if the serialized URL string should include the search query, false otherwise. |
$options.unicodeoptional | boolean= false | true if Unicode characters appearing in the host component of the URL string should be encoded directly as opposed to being Punycode encoded. |
Return value
stringA customizable serialization of a URL String representation of a WHATWG URL object.
Errors
Throws ERR_INVALID_ARG_TYPE when the first argument is neither a WHATWG URL object nor (for the legacy overload) an object or string.
Changelog
- PHP v24.0.0The legacy url.format(urlString) form received an application deprecation; url.format(urlObject) remains Legacy (stability 3).
- PHP v17.0.0The legacy overload now throws ERR_INVALID_URL when Punycode conversion of a hostname introduces changes that could cause the URL to be re-parsed differently.
- PHP v15.13.0, v14.17.0Deprecation of the legacy URL API was revoked; its status changed to Legacy.
url.format() has two unrelated overloads. The one worth using takes a WHATWG URL object plus an options bag and returns a serialization with chosen parts omitted. The legacy overload takes a plain object of the shape url.parse() returns; passing it a string is deprecated outright.
When to reach for it
Reach for the WHATWG form when you need a variant of a URL rather than its full href. Typical cases: { auth: false } before writing a connection URL to a log so the password does not leak; { fragment: false } to build a canonical link because servers never see the fragment anyway; { search: false } to group requests by endpoint in metrics; { unicode: true } to show an internationalized host to a human as пример.example instead of xn--e1afmkfd.example.
What trips people up
With no options it is simply url.href, so reaching for it there adds an import for nothing. Each option only removes or re-renders a component — there is no option to add one, and no option for the port or path.
unicode: true is display-only. Never feed that output back to new URL() for security decisions, because the Unicode and Punycode forms are different strings and homograph lookalikes compare unequal in confusing ways.
Example
When to use it
- Redact credentials from a database URL before logging it
- Build a canonical link by dropping the fragment
- Group request metrics by endpoint by stripping the query string
- Render an internationalized domain in its readable Unicode form for a UI
Frequently asked questions
How do I remove the fragment or query from a URL in Node?
Call url.format(myUrl, { fragment: false }) or { search: false } on a WHATWG URL object. You can also assign empty strings to url.hash and url.search, which mutates the object instead of returning a variant.
Is url.format deprecated?
The WHATWG overload, url.format(URL[, options]), is current and not deprecated. The legacy url.format(urlObject) is stability 3 (Legacy), and url.format(urlString) received an application deprecation in v24.0.0.
What is the difference between url.format(u) and u.href?
Nothing, when no options are passed — they produce the same string. url.format only earns its keep when you pass options to omit auth, search or fragment, or to render the host in Unicode.
More examples
Redacting credentials before logging
auth: false drops both username and password while keeping host, port, path and query, so the log line stays useful for debugging. The last line shows what would have leaked with a plain href.
const url = require('node:url');
function safeToLog(connectionString) {
return url.format(new URL(connectionString), { auth: false });
}
console.log(safeToLog('postgres://app:[email protected]:5432/prod?sslmode=require'));
console.log(safeToLog('redis://:[email protected]:6379/0'));
console.log(new URL('postgres://app:[email protected]:5432/prod').href);Canonical URLs for deduplication
Fragments are client-side only, so two links differing just in #comments vs #share point at the same resource. Sorting the parameters and dropping the fragment makes both collapse to one key.
const url = require('node:url');
function canonical(href) {
const u = new URL(href);
u.searchParams.sort();
return url.format(u, { fragment: false });
}
const a = canonical('https://example.com/post?b=2&a=1#comments');
const b = canonical('https://example.com/post?a=1&b=2#share');
console.log(a);
console.log(b);
console.log('same page?', a === b);The legacy object overload
The legacy form still works and adds the '://' and '?' for you, but it accepts a loose object with no validation. The WHATWG version below it produces the same string while checking the result is a real URL at every step.
const url = require('node:url');
// Legacy: format an object of the shape url.parse() returns.
console.log(url.format({
protocol: 'https',
hostname: 'example.com',
pathname: '/a/b',
query: { x: '1', y: '2' },
hash: '#top'
}));
// The modern equivalent.
const u = new URL('https://example.com/a/b');
u.search = new URLSearchParams({ x: '1', y: '2' }).toString();
u.hash = 'top';
console.log(u.href);
Discussion