Question
I want to get the full current URL in PHP exactly as it appears in the browser's address bar.
I currently use this code:
$actual_link = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
However, this does not work correctly when I use URL rewriting rules in .htaccess, because the visible URL is not always the real file path on the server.
I need the URL exactly as the user sees it in the browser, including the rewritten path rather than the underlying server file path.
Short Answer
By the end of this page, you will understand how PHP builds the current request URL, why $_SERVER['PHP_SELF'] can be misleading with rewritten routes, and how to reliably get the full browser-visible URL using the correct server variables.
Concept
In PHP, the current URL is usually reconstructed from values in the $_SERVER superglobal. The important detail is that not all server variables represent the same thing.
For example:
$_SERVER['PHP_SELF']often points to the executing script path.$_SERVER['REQUEST_URI']contains the path and query string that the browser actually requested.$_SERVER['HTTP_HOST']contains the requested host name.$_SERVER['HTTPS']helps determine whether the request usedhttporhttps.
This matters because URL rewriting tools such as Apache .htaccess rules can map a friendly URL like:
/products/42
to a real server file such as:
/index.php?id=42
If you use PHP_SELF, you may get the script path instead of the URL the user typed or clicked. If you want the browser-visible URL, REQUEST_URI is usually the correct choice.
A common way to build the full URL is:
$url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http')
. '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
This works because:
- the scheme comes from
HTTPS - the domain comes from
HTTP_HOST - the visible path and query come from
REQUEST_URI
In real programming, understanding these differences is important for:
- redirects
- canonical URLs
- logging requests
- debugging routing issues
- generating links back to the current page
Mental Model
Think of a web request like a package delivery label:
HTTP_HOSTis the building name.REQUEST_URIis the apartment number and any delivery instructions.PHP_SELFis the internal office room where the package was actually processed.
If you want to know what the visitor asked for, use the delivery label they wrote: REQUEST_URI.
If you want to know which PHP file handled it internally, use something like PHP_SELF.
With URL rewriting, those two can be different.
Syntax and Examples
The most common beginner-friendly approach is:
<?php
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
echo $url;
Example
If the browser shows:
https://example.com/blog/php?sort=new
then the variables are commonly like this:
$_SERVER['HTTPS']->on$_SERVER['HTTP_HOST']->example.com$_SERVER['REQUEST_URI']->/blog/php?sort=new
So the final result becomes:
https://example.com/blog/php?sort=new
Why PHP_SELF is different
<?php
echo $_SERVER['PHP_SELF'];
This may output something like:
/index.php
That is the script being executed, not necessarily the friendly URL requested by the user.
Safer reusable function
<?php
function getCurrentUrl(): string
{
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
return $scheme . '://' . $host . $uri;
}
echo getCurrentUrl();
This version adds fallback values so your code is less fragile.
Step by Step Execution
Consider this code:
<?php
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
$uri = $_SERVER['REQUEST_URI'];
$currentUrl = $scheme . '://' . $host . $uri;
echo $currentUrl;
Assume the browser requested:
https://shop.example.com/products/15?page=2
Step-by-step
-
PHP checks
$_SERVER['HTTPS'].- It is set and not equal to
'off'. - So
$schemebecomes'https'.
- It is set and not equal to
-
PHP reads
$_SERVER['HTTP_HOST'].$hostbecomes'shop.example.com'.
-
PHP reads
$_SERVER['REQUEST_URI'].$uribecomes'/products/15?page=2'.
-
PHP concatenates the parts:
$currentUrl = 'https' . '://' . 'shop.example.com' . '/products/15?page=2';
- Final result:
https://shop.example.com/products/15?page=2
Rewritten URL example
Suppose Apache internally routes this request to:
/index.php?product=15&page=2
Even if that is the real server-side target, REQUEST_URI still reflects the URL requested by the browser:
/products/15?page=2
That is why it is the better choice for this problem.
Real World Use Cases
Getting the full current URL is useful in many real applications:
-
Redirect back after login
- Store the current page so users return to it after authenticating.
-
Canonical URL generation
- Build the preferred page URL for SEO.
-
Analytics and logging
- Save the exact requested URL for debugging or traffic reports.
-
Sharing links
- Generate a copyable link to the current page.
-
Pagination and filters
- Preserve the current path and query string when building navigation links.
-
Error reporting
- Include the exact request URL in logs when something fails.
Example logging use:
<?php
error_log('User visited: ' . getCurrentUrl());
Real Codebase Usage
In real projects, developers usually do not build URLs inline everywhere. Instead, they wrap the logic in a helper function or service.
Common patterns include:
Guard clauses for missing values
<?php
function getCurrentUrl(): string
{
if (empty($_SERVER['HTTP_HOST']) || empty($_SERVER['REQUEST_URI'])) {
return '';
}
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
return $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
Validation before redirects
Developers often use the current URL as a return_url, but validate it first to avoid unsafe redirects.
<?php
$returnUrl = getCurrentUrl();
if ($returnUrl !== '') {
$_SESSION['return_url'] = $returnUrl;
}
Query string preservation
When building filtered pages, teams often read REQUEST_URI to keep the original query string intact.
Framework abstraction
In modern codebases, frameworks often provide a request object instead of direct $_SERVER access. But underneath, the same idea applies: use the browser-requested URI, not the physical file path.
Error handling and diagnostics
Many applications log the requested URL during exceptions so debugging is easier:
<?php
try {
// application code
} catch (Throwable $e) {
error_log('Error on URL: ' . getCurrentUrl());
throw $e;
}
Common Mistakes
1. Using PHP_SELF for the visible URL
Broken example:
<?php
$url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
Why it is a problem:
PHP_SELFmay point to the executing script.- It may not match the rewritten route the user sees.
Use this instead:
<?php
$url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
2. Hardcoding http
Broken example:
<?php
$url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
Why it is a problem:
- It gives the wrong result on HTTPS pages.
Better:
<?php
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
3. Forgetting the query string
Broken example:
<?php
$url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
Why it is a problem:
- Query parameters like
?page=2are lost. REQUEST_URIincludes them.
4. Assuming all $_SERVER keys always exist
Broken example:
<?php
$url = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
Why it is a problem:
- In some environments, values may be missing.
- This can trigger notices.
Safer version:
<?php
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
5. Trusting the URL for security decisions
Why it is a problem:
- Request values come from the client and environment.
- Do not assume they are safe for redirects, HTML output, or access control without validation and escaping.
Comparisons
| Variable / Approach | What it represents | Includes query string? | Good for browser-visible URL? |
|---|---|---|---|
$_SERVER['PHP_SELF'] | Executing script path | No | No |
$_SERVER['REQUEST_URI'] | Requested path from browser | Yes | Yes |
$_SERVER['SCRIPT_NAME'] | Script filename path | No | No |
$_SERVER['HTTP_HOST'] | Host name from request | No | Only as one part |
$_SERVER['QUERY_STRING'] | Only the query part | Yes, but only query | No |
PHP_SELF vs REQUEST_URI
- Use
PHP_SELFwhen you need the script path being executed. - Use
REQUEST_URIwhen you need the path the browser requested.
HTTP_HOST vs SERVER_NAME
HTTP_HOSTusually reflects the host requested by the client.SERVER_NAMEmay reflect server configuration instead.- For reconstructing the current URL,
HTTP_HOSTis commonly preferred.
Full URL formula
$fullUrl = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
This is usually the closest match to what the browser shows.
Cheat Sheet
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$fullUrl = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
Quick rules
- Use
REQUEST_URIfor the browser-requested path. - Do not use
PHP_SELFif URL rewriting is involved. - Use
HTTPSto detecthttpvshttps. - Use
HTTP_HOSTfor the domain part. REQUEST_URIusually includes the query string.
Common variables
$_SERVER['HTTP_HOST']-> host/domain$_SERVER['REQUEST_URI']-> path + query string$_SERVER['PHP_SELF']-> executing script path$_SERVER['QUERY_STRING']-> query only$_SERVER['HTTPS']-> whether HTTPS is active
Safe helper
function getCurrentUrl(): string
{
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
return $scheme . '://' . $host . $uri;
}
Watch out for
- rewritten routes
- missing server keys
- hardcoded
http - using request data without validation
FAQ
How do I get the current full URL in PHP?
Use HTTP_HOST, REQUEST_URI, and the request scheme:
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
Why is $_SERVER['PHP_SELF'] not giving the visible URL?
PHP_SELF usually points to the script being executed on the server, not the rewritten or friendly URL requested by the browser.
Does REQUEST_URI include the query string?
Yes, it usually includes both the path and the query string, such as /products?page=2.
How do I detect HTTPS in PHP?
A common check is:
!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'
Should I use HTTP_HOST or SERVER_NAME?
For reconstructing the requested URL, HTTP_HOST is commonly more appropriate because it reflects the host sent by the client.
Can URL rewriting change what PHP_SELF returns?
Yes. With rewritten routes, the public URL and the internal script path can be different, which is exactly why REQUEST_URI is often the better choice.
Is the current URL safe to output directly in HTML?
Not always. If you print it into a page, escape it first with htmlspecialchars().
Can I use this approach in frameworks?
Yes, but frameworks often provide a request object with methods for getting the current URL, path, and query string more cleanly.
Mini Project
Description
Build a small PHP page that displays the exact current URL requested by the browser. This demonstrates how to combine scheme, host, and request URI correctly, even when friendly URLs or rewritten routes are used.
Goal
Create a reusable PHP function that prints the full current URL exactly as the user requested it.
Requirements
- Create a function that returns the current full URL.
- Detect whether the request uses HTTP or HTTPS.
- Include the host name and full request URI.
- Print the URL on the page.
- Handle missing server values with sensible fallbacks.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.