url.parse()

Parses a URL string into a legacy URL object; deprecated in favour of the WHATWG URL constructor.

Signature
url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
  • Added in Node.js v0.1.25
  • Deprecated since PHP v19.0.0, v18.13.0

Parameters

ParameterTypeDescription
$urlStringstringThe URL string to parse.
$parseQueryStringoptionalboolean= falseIf true, the query property will always be set to an object returned by the querystring module's parse() method. If false, the query property on the returned URL object will be an unparsed, undecoded string.
$slashesDenoteHostoptionalboolean= falseIf true, the first token after the literal string '//' and preceding the next '/' will be interpreted as the host. For instance, given //foo/bar, the result would be {host: 'foo', pathname: '/bar'} rather than {pathname: '//foo/bar'}.

Return value

ObjectA legacy urlObject with properties such as protocol, slashes, auth, host, port, hostname, hash, search, query, pathname, path and href.

Errors

Throws a TypeError if urlString is not a string, and ERR_INVALID_URL for a few malformed inputs — but most invalid URLs are accepted and produce a nonsensical object rather than an error.

Changelog

  • PHP v24.0.0Application deprecation: a runtime deprecation warning is now emitted.
  • PHP v19.9.0, v18.17.0Added support for --pending-deprecation, which surfaces a warning on use.
  • PHP v19.0.0, v18.13.0Documentation-only deprecation.
  • PHP v15.13.0, v14.17.0Deprecation revoked; status changed to Legacy.
  • PHP v11.14.0The pathname property is now '/' when there is no path and the protocol scheme is ws: or wss:.
  • PHP v9.0.0The search property is now null when no query string is present (it was previously an empty string).

url.parse() is the pre-WHATWG parser that shipped in Node 0.1. It is documented here because old code and old tutorials are full of it, not because you should reach for it: the docs mark it stability 0 — Deprecated, and Node 24 emits a runtime warning when it is called.

Migrating

The mapping is mostly mechanical. protocol, hostname, port, pathname, search and hash exist on both. The differences: legacy path (pathname + search) has no equivalent — build it as u.pathname + u.search. Legacy query becomes u.searchParams, and auth splits into u.username and u.password. Legacy search and hash are null when absent, whereas WHATWG returns ''.

Why it is dangerous

It is lenient where the WHATWG parser is strict, and that leniency has produced real security advisories. url.parse('example.com/a/b') returns host: null with the whole thing as pathname, so an origin check written against host compares null and passes. It also does not normalize, does not validate, and never throws on the inputs that matter — an attacker-supplied string that new URL() would reject sails straight through.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Read code written before Node 7 that still uses the legacy urlObject shape
  • Migrate an existing url.parse call site to new URL() safely
  • Understand a bug where a legacy parse accepted input the WHATWG parser rejects
  • Use req.url's path property in older Express-era middleware

Frequently asked questions

Is url.parse deprecated in Node?

Yes — it is stability 0, Deprecated, since v19.0.0/v18.13.0 as documentation-only, and it emits a runtime deprecation warning as of v24.0.0. It has not been removed, so existing code keeps working, but new code should use new URL().

What should I use instead of url.parse?

Use the WHATWG URL constructor: new URL(str), or new URL(req.url, 'http://localhost') for the path-only URLs an http server gives you. Read the query through url.searchParams rather than the legacy query object.

Why does url.parse not throw on invalid URLs?

It was written to be maximally permissive and treats anything it cannot recognize as a path, returning null for host. That is exactly why it is unsafe for validation — use URL.canParse() or new URL() in a try/catch instead.

More examples

The leniency that breaks origin checks

url.parse happily returns an object with no host, so a check like host !== 'evil.example' passes for a string that is not a URL at all. new URL throws instead, forcing the caller to handle bad input explicitly.

Example · javascript
const url = require('node:url');

const input = 'example.com/a/b';

const legacy = url.parse(input);
console.log('legacy host:', JSON.stringify(legacy.host));
console.log('legacy pathname:', JSON.stringify(legacy.pathname));

try {
  new URL(input);
} catch (err) {
  console.log('WHATWG:', err.code);
}

console.log('bad allowlist passes?', legacy.host !== 'evil.example');

Migrating a request handler

req.url on an http server is path-only, so new URL needs a base — any placeholder origin works. The legacy query object keeps only the last tag, while searchParams.getAll returns both.

Example · javascript
const url = require('node:url');

const reqUrl = '/api/items?page=2&tag=a&tag=b';

// Old style.
const old = url.parse(reqUrl, true);
console.log(old.pathname, JSON.stringify(old.query));

// New style: give the relative path a dummy base.
const next = new URL(reqUrl, 'http://localhost');
console.log(next.pathname, Object.fromEntries(next.searchParams));
console.log('all tags:', next.searchParams.getAll('tag'));

Absent components: null vs empty string

The legacy object uses null for missing pieces while WHATWG uses empty strings. Code that tested `if (parsed.search === null)` needs to become `if (u.search === '')` during migration, or it will read a missing query as present.

Example · javascript
const url = require('node:url');

const legacy = url.parse('https://example.com/a');
console.log(JSON.stringify([legacy.search, legacy.query, legacy.hash, legacy.auth]));

const modern = new URL('https://example.com/a');
console.log(JSON.stringify([modern.search, modern.hash, modern.username, modern.password]));

Discussion

  • Be the first to comment on this lesson.