parse_url
Splits a URL into its scheme, host, port, path, query and fragment components.
parse_url(string $url, int $component = -1): int|string|array|null|false- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$url | string | The URL to parse. Invalid characters are replaced by underscores. |
$componentoptional | int= -1 | One 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
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.
<?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.
<?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.
<?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