Get the full URL in PHP: How Not to Reinvent the Wheel and Lose Performance
\n\nEvery PHP developer sooner or later faces the task: "How do I get the full URL of the current page?" It seems simple enough — take $_SERVER['REQUEST_URI'], add the domain, and you're done. But the devil, as always, is in the details. The question "Get the full URL in PHP" on Stack Overflow has gathered thousands of views and dozens of answers. And for good reason.
In this article, we'll break down how to get the full URL in PHP correctly, quickly, and without bugs. You'll learn the difference between HTTP_HOST and SERVER_NAME, how to correctly determine the protocol (HTTPS or HTTP), and why you shouldn't blindly copy code from the internet. Let's go!
Why the Simple Way Isn't Always the Best
\n\nThe most obvious option looks like this:
\n\n<?php\n$fullUrl = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];\n?>\n\nThis code works, but only if you have:
\n- \n
- Definitely HTTP (not HTTPS) \n
- No port other than 80 or 443 \n
- The server correctly passes the Host header \n
In the real world, these conditions are not always met. If the site runs over HTTPS, you'll get a URL with http://, which will cause a mixed content error in the browser. If the port is non-standard (e.g., 8080), it will be lost. And if an attacker spoofs the Host header, you could get a URL pointing to a different domain.
\n\nComponents of a Full URL: A Breakdown
\n\nBefore writing a universal solution, let's recall what a full URL consists of:
\n\nscheme://host:port/path?query#fragment\n\nIn PHP, we have access to the following elements from the superglobal array $_SERVER:
- \n
- scheme — protocol (HTTP or HTTPS). There is no direct key, but it can be determined via
$_SERVER['HTTPS']or$_SERVER['REQUEST_SCHEME'](available since PHP 7.4). \n - host — domain. There are two candidates:
HTTP_HOST(from the Host header) andSERVER_NAME(from the server configuration). \n - port — port.
$_SERVER['SERVER_PORT']. If the port is standard (80 for HTTP, 443 for HTTPS), it can be omitted. \n - path — path.
$_SERVER['REQUEST_URI']contains the path and query string. \n - query — query string. Can be obtained separately via
$_SERVER['QUERY_STRING']. \n - fragment — anchor. It is not sent to the server, so it cannot be obtained. \n
The Correct Solution: The getFullUrl() Function
\n\nHere is a reliable and performant way to get the full URL in PHP:
\n\n<?php\nfunction getFullUrl(): string {\n // Determine the protocol\n $scheme = 'http';\n if (\n (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] === '1'))\n || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')\n ) {\n $scheme = 'https';\n }\n\n // Host: prefer HTTP_HOST, but check for emptiness\n $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';\n\n // Port: add only if non-standard\n $port = (int) ($_SERVER['SERVER_PORT'] ?? 80);\n $portStr = '';\n if (\n ($scheme === 'http' && $port !== 80) ||\n ($scheme === 'https' && $port !== 443)\n ) {\n $portStr = ':' . $port;\n }\n\n // URI\n $uri = $_SERVER['REQUEST_URI'] ?? '/';\n\n return $scheme . '://' . $host . $portStr . $uri;\n}\n?>\n\nThis function accounts for:
\n- \n
- HTTPS protocol (including through proxy servers with the
X-Forwarded-Protoheader). \n - Non-standard ports. \