parse_url

Splits a URL into its scheme, host, port, path, query and fragment components.

Signature
parse_url(string $url, int $component = -1): int|string|array|null|false
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$urlstringThe URL to parse. Invalid characters are replaced by underscores.
$componentoptionalint= -1One of PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_USER, PHP_URL_PASS, PHP_URL_PATH, PHP_URL_QUERY or PHP_URL_FRAGMENT to return just that piece.

Return value

int|string|array|null|falseAn associative array of the present components, or the single requested component as a string (int for the port), null if that component is absent, or false on a seriously malformed URL.

Errors

Does not throw; returns false for seriously malformed URLs.

Changelog

  • PHP 8.0.0parse_url() now distinguishes absent from empty queries and fragments: 'http://example.com/foo?' yields an empty query string, while 'http://example.com/foo' yields no query key at all.

parse_url() breaks a URL into named parts. Keys appear in the returned array only when that component exists, so always use ?? or isset() rather than assuming $parts['port'] is there.

It parses, it does not validate

This is the single biggest misunderstanding. parse_url() happily accepts strings no browser would fetch, and it does not check that a host resolves or that a scheme is real. For validation use filter_var($url, FILTER_VALIDATE_URL), and for security decisions always compare the host component, never str_contains() on the whole URL — https://evil.test/?x=example.com contains your domain but is not it.

Values are not decoded

The returned strings keep their percent-encoding. A path of /a%20b stays /a%20b; run it through rawurldecode() if you need the literal characters. The query component is one raw string — feed it to parse_str() to get an array.

Relative URLs

Given /blog/post?id=1 you get only path and query, with no scheme or host. That absence is exactly how you detect a relative reference.

Example

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

When to use it

  • Extract the host to check a redirect target belongs to your own domain
  • Pull the path out of a full URL to route it or look it up in a sitemap
  • Change one query parameter on an existing URL and rebuild it
  • Detect whether a link in user content is relative or absolute

Frequently asked questions

Does parse_url validate a URL?

No. It only splits the string and returns false for seriously malformed input. Use filter_var($url, FILTER_VALIDATE_URL) when you need validation.

Why is there no 'port' key in my result?

Keys appear only for components actually present in the URL. Use $parts['port'] ?? 443 instead of indexing directly.

How do I get the query parameters as an array?

parse_url returns the query as one raw string; pass it to parse_str($query, $params) to get an associative array.

More examples

Rebuilding a URL with one parameter changed

parse_url splits the URL, parse_str turns the raw query into an array, and http_build_query re-serialises it after page is bumped. The ?? '' guards the case where the original URL had no query at all.

Example · php
<?php
$url = 'https://example.com/search?q=php&page=1';
$parts = parse_url($url);

parse_str($parts['query'] ?? '', $params);
$params['page'] = 2;

$rebuilt = $parts['scheme'] . '://' . $parts['host']
    . ($parts['path'] ?? '')
    . '?' . http_build_query($params);

echo $rebuilt, "\n";

Checking a redirect target safely

The relative path has no host, so it is internal. The exact host match passes. The last two are rejected: a substring search for "example.com" would have wrongly accepted the third, and the protocol-relative //evil.test/x parses to host evil.test, which is the classic open-redirect trap.

Example · php
<?php
function isInternal(string $url, string $allowed): bool {
    $host = parse_url($url, PHP_URL_HOST);
    return $host === null || strcasecmp($host, $allowed) === 0;
}

var_dump(isInternal('/account/settings', 'example.com'));
var_dump(isInternal('https://example.com/x', 'example.com'));
var_dump(isInternal('https://evil.test/?to=example.com', 'example.com'));
var_dump(isInternal('//evil.test/x', 'example.com'));

Components are still percent-encoded

The raw path keeps %20 and %2B untouched; only rawurldecode turns them into a space and a plus. parse_str, by contrast, decodes query values for you, which is why $q['name'] prints "a b" directly.

Example · php
<?php
$url = 'https://example.com/files/my%20report%2Bfinal.pdf?name=a%20b';
$parts = parse_url($url);

echo $parts['path'], "\n";
echo rawurldecode($parts['path']), "\n";

parse_str($parts['query'], $q);
echo $q['name'], "\n";

Discussion

  • Be the first to comment on this lesson.