PHP $_SERVER['HTTP_HOST'] 与 $_SERVER['SERVER_NAME'],我是否正确理解手册页?

发布于 2024-08-04 23:49:10 字数 1212 浏览 5 评论 0原文

我进行了大量搜索,还阅读了 PHP $_SERVER 文档。我是否有权决定在我的 PHP 脚本中使用哪些脚本来定义整个站点中使用的简单链接?

$_SERVER['SERVER_NAME'] 基于您的 Web 服务器的配置文件(在我的例子中是 Apache2),并根据以下几个指令而变化:(1) VirtualHost、(2) ServerName、(3) UseCanonicalName等。

$_SERVER['HTTP_HOST']是根据客户端的请求来的。

因此,在我看来,为了使我的脚本尽可能兼容,最合适使用的是 $_SERVER['HTTP_HOST']。这个假设正确吗?

后续评论:

我想在读完这篇文章并注意到有些人说“他们不会信任任何 $_SERVER vars”后我有点偏执:

显然讨论主要是关于 $_SERVER['PHP_SELF'] 以及为什么你不应该在表单action属性中使用它而不进行适当的转义以防止XSS攻击。

我对上述原始问题的结论是,对网站上的所有链接使用 $_SERVER['HTTP_HOST'] 是“安全”的,无需担心 XSS 攻击,即使在表单中使用也是如此。

如果我错了,请纠正我。

I did a lot of searching and also read the PHP $_SERVER docs. Do I have this right regarding which to use for my PHP scripts for simple link definitions used throughout my site?

$_SERVER['SERVER_NAME'] is based on your web server's config file (Apache2 in my case), and varies depending on a few directives: (1) VirtualHost, (2) ServerName, (3) UseCanonicalName, etc.

$_SERVER['HTTP_HOST'] is based on the request from the client.

Therefore, it would seem to me that the proper one to use in order to make my scripts as compatible as possible would be $_SERVER['HTTP_HOST']. Is this assumption correct?

Followup comments:

I guess I got a little paranoid after reading this article and noting that some folks said "they wouldn't trust any of the $_SERVER vars":

Apparently the discussion is mainly about $_SERVER['PHP_SELF'] and why you shouldn't use it in the form action attribute without proper escaping to prevent XSS attacks.

My conclusion about my original question above is that it is "safe" to use $_SERVER['HTTP_HOST'] for all links on a site without having to worry about XSS attacks, even when used in forms.

Please correct me if I'm wrong.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

夜清冷一曲。 2024-08-11 23:49:10

这想必是每个人的第一想法。但这有点困难。请参阅 Chris Shiflett 的文章SERVER_NAMEHTTP_HOST 相比。

似乎没有灵丹妙药。仅当您强制 Apache 使用规范名称时,您将始终使用 SERVER_NAME 获取正确的服务器名称。

因此,您要么接受它,要么根据白名单检查主机名:

$allowed_hosts = array('foo.example.com', 'bar.example.com');
if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $allowed_hosts)) {
    header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
    exit;
}

That’s probably everyone’s first thought. But it’s a little bit more difficult. See Chris Shiflett’s article SERVER_NAME Versus HTTP_HOST.

It seems that there is no silver bullet. Only when you force Apache to use the canonical name you will always get the right server name with SERVER_NAME.

So you either go with that or you check the host name against a white list:

$allowed_hosts = array('foo.example.com', 'bar.example.com');
if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $allowed_hosts)) {
    header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
    exit;
}
江挽川 2024-08-11 23:49:10

附加说明 - 如果服务器在 80 以外的端口上运行(这在开发/内联网计算机上可能很常见),则 HTTP_HOST 包含该端口,而 SERVER_NAME 则包含该端口不是。

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(至少这是我在基于 Apache 端口的虚拟主机中注意到的)

正如 Mike 在下面指出的,当在 HTTPS 上运行(除非您在非标准端口上运行,我尚未测试过)。

Just an additional note - if the server runs on a port other than 80 (as might be common on a development/intranet machine) then HTTP_HOST contains the port, while SERVER_NAME does not.

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(At least that's what I've noticed in Apache port-based virtualhosts)

As Mike has noted below, HTTP_HOST does not contain :443 when running on HTTPS (unless you're running on a non-standard port, which I haven't tested).

栀梦 2024-08-11 23:49:10

使用任一。它们都同样(不)安全,因为在许多情况下 SERVER_NAME 无论如何都是从 HTTP_HOST 填充的。我通常选择 HTTP_HOST,以便用户停留在他们开始时使用的确切主机名上。例如,如果我在 .com 和 .org 域上有相同的网站,我不想将某人从 .org 发送到 .com,特别是如果他们可能在 .org 上拥有登录令牌,如果发送到这些令牌,他们就会丢失另一个域。

无论哪种方式,您只需要确保您的网络应用程序只会响应已知良好的域。这可以通过 (a) 通过像 Gumbo 这样的应用程序端检查来完成,或者 (b) 通过在您希望不响应请求的域名上使用虚拟主机来完成未知的主机标头。

这样做的原因是,如果您允许以任何旧名称访问您的站点,您就会面临 DNS 重新绑定攻击(其中另一个站点的主机名指向您的 IP,用户使用攻击者的主机名访问您的站点,然后使用该主机名)移动到攻击者的 IP,并带走您的 cookie/身份验证)和搜索引擎劫持(攻击者将自己的主机名指向您的站点,并尝试让搜索引擎将其视为“最佳”主要主机名)。

显然,讨论主要是关于 $_SERVER['PHP_SELF'] 以及为什么不应该在没有正确转义的情况下在表单操作属性中使用它来防止 XSS 攻击。

噗。好吧,如果不使用 htmlspecialchars($string, ENT_QUOTES) 进行转义,则不应在 any 属性中使用 anything,因此服务器变量没有什么特别之处。

Use either. They are both equally (in)secure, as in many cases SERVER_NAME is just populated from HTTP_HOST anyway. I normally go for HTTP_HOST, so that the user stays on the exact host name they started on. For example if I have the same site on a .com and .org domain, I don't want to send someone from .org to .com, particularly if they might have login tokens on .org that they'd lose if sent to the other domain.

Either way, you just need to be sure that your webapp will only ever respond for known-good domains. This can be done either (a) with an application-side check like Gumbo's, or (b) by using a virtual host on the domain name(s) you want that does not respond to requests that give an unknown Host header.

The reason for this is that if you allow your site to be accessed under any old name, you lay yourself open to DNS rebinding attacks (where another site's hostname points to your IP, a user accesses your site with the attacker's hostname, then the hostname is moved to the attacker's IP, taking your cookies/auth with it) and search engine hijacking (where an attacker points their own hostname at your site and tries to make search engines see it as the ‘best’ primary hostname).

Apparently the discussion is mainly about $_SERVER['PHP_SELF'] and why you shouldn't use it in the form action attribute without proper escaping to prevent XSS attacks.

Pfft. Well you shouldn't use anything in any attribute without escaping with htmlspecialchars($string, ENT_QUOTES), so there's nothing special about server variables there.

孤独患者 2024-08-11 23:49:10

这是 Symfony 用于获取主机名的详细翻译(请参阅第二个示例以获取更直译的翻译):

function getHost() {
    $possibleHostSources = array('HTTP_X_FORWARDED_HOST', 'HTTP_HOST', 'SERVER_NAME', 'SERVER_ADDR');
    $sourceTransformations = array(
        "HTTP_X_FORWARDED_HOST" => function($value) {
            $elements = explode(',', $value);
            return trim(end($elements));
        }
    );
    $host = '';
    foreach ($possibleHostSources as $source)
    {
        if (!empty($host)) break;
        if (empty($_SERVER[$source])) continue;
        $host = $_SERVER[$source];
        if (array_key_exists($source, $sourceTransformations))
        {
            $host = $sourceTransformations[$source]($host);
        } 
    }

    // Remove port number from host
    $host = preg_replace('/:\d+$/', '', $host);

    return trim($host);
}

已过时:

是我对 Symfony 框架中使用的方法的裸 PHP 的翻译,该方法尝试按照最佳实践的顺序从各种可能的方式获取主机名:

function get_host() {
    if ($host = $_SERVER['HTTP_X_FORWARDED_HOST'])
    {
        $elements = explode(',', $host);

        $host = trim(end($elements));
    }
    else
    {
        if (!$host = $_SERVER['HTTP_HOST'])
        {
            if (!$host = $_SERVER['SERVER_NAME'])
            {
                $host = !empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '';
            }
        }
    }

    // Remove port number from host
    $host = preg_replace('/:\d+$/', '', $host);

    return trim($host);
}

This is a verbose translation of what Symfony uses to get the host name (see the second example for a more literal translation):

function getHost() {
    $possibleHostSources = array('HTTP_X_FORWARDED_HOST', 'HTTP_HOST', 'SERVER_NAME', 'SERVER_ADDR');
    $sourceTransformations = array(
        "HTTP_X_FORWARDED_HOST" => function($value) {
            $elements = explode(',', $value);
            return trim(end($elements));
        }
    );
    $host = '';
    foreach ($possibleHostSources as $source)
    {
        if (!empty($host)) break;
        if (empty($_SERVER[$source])) continue;
        $host = $_SERVER[$source];
        if (array_key_exists($source, $sourceTransformations))
        {
            $host = $sourceTransformations[$source]($host);
        } 
    }

    // Remove port number from host
    $host = preg_replace('/:\d+$/', '', $host);

    return trim($host);
}

Outdated:

This is my translation to bare PHP of a method used in Symfony framework that tries to get the hostname from every way possible in order of best practice:

function get_host() {
    if ($host = $_SERVER['HTTP_X_FORWARDED_HOST'])
    {
        $elements = explode(',', $host);

        $host = trim(end($elements));
    }
    else
    {
        if (!$host = $_SERVER['HTTP_HOST'])
        {
            if (!$host = $_SERVER['SERVER_NAME'])
            {
                $host = !empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '';
            }
        }
    }

    // Remove port number from host
    $host = preg_replace('/:\d+$/', '', $host);

    return trim($host);
}
同尘 2024-08-11 23:49:10

对网站上的所有链接使用 $_SERVER['HTTP_HOST'] 是否“安全”,而不必担心 XSS 攻击,即使在表单中使用也是如此?

是的,使用 $_SERVER['HTTP_HOST'] 安全 code>(甚至是 $_GET$_POST只要您在接受之前验证它们即可。这就是我对安全生产服务器所做的事情:

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
$reject_request = true;
if(array_key_exists('HTTP_HOST', $_SERVER)){
    $host_name = $_SERVER['HTTP_HOST'];
    // [ need to cater for `host:port` since some "buggy" SAPI(s) have been known to return the port too, see http://goo.gl/bFrbCO
    $strpos = strpos($host_name, ':');
    if($strpos !== false){
        $host_name = substr($host_name, $strpos);
    }
    // ]
    // [ for dynamic verification, replace this chunk with db/file/curl queries
    $reject_request = !array_key_exists($host_name, array(
        'a.com' => null,
        'a.a.com' => null,
        'b.com' => null,
        'b.b.com' => null
    ));
    // ]
}
if($reject_request){
    // log errors
    // display errors (optional)
    exit;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
echo 'Hello World!';
// ...

$_SERVER['HTTP_HOST'] 的优点是它的行为比 $_SERVER['SERVER_NAME'] 定义更明确>。对比 ➫➫

主机内容:当前请求的标头(如果有)。

和:

当前脚本正在执行的服务器主机的名称。

使用像 $_SERVER['HTTP_HOST'] 这样定义更好的接口意味着更多的 SAPI 将使用可靠定义良好的行为来实现它。 (与另一个不同。)但是,它仍然完全依赖于SAPI ➫➫

不保证每个网络服务器都会提供这些 [$_SERVER 条目];服务器可能会省略一些,或提供此处未列出的其他内容。

要了解如何正确检索主机名,首先您需要了解仅包含代码的服务器无法知道(验证的先决条件)其自己的名称< /strong> 在网络上。它需要与为其提供自己名称的组件进行交互。这可以通过以下方式完成:

  • 本地配置文件

  • 本地数据库

  • 硬编码源代码

  • 外部请求(curl

  • 客户端/攻击者的 Host: 请求

  • etc

通常是通过本地(SAPI)配置文件完成的。请注意,您已正确配置它,例如在 Apache ➫➫ 中:

需要“伪造”一些东西才能使动态虚拟主机看起来像普通主机。

最重要的是服务器名称,Apache 使用它来生成自引用 URL 等。它是使用 ServerName 指令配置的,并且可以通过 CGI 访问它。 SERVER_NAME 环境变量。

运行时使用的实际值 UseCanonicalName 设置控制。

使用 UseCanonicalName Off 服务器名称来自请求中 Host: 标头的内容。 使用 UseCanonicalName DNS 它来自虚拟主机 IP 地址的反向 DNS 查找。前一个设置用于基于名称的动态虚拟主机,后者用于**基于IP的主机。

如果 Apache 无法计算出服务器名称,因为没有 Host: 标头或 DNS 查找失败使用<配置的值改为使用 code>ServerName。

Is it "safe" to use $_SERVER['HTTP_HOST'] for all links on a site without having to worry about XSS attacks, even when used in forms?

Yes, it's safe to use $_SERVER['HTTP_HOST'], (and even $_GET and $_POST) as long as you verify them before accepting them. This is what I do for secure production servers:

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
$reject_request = true;
if(array_key_exists('HTTP_HOST', $_SERVER)){
    $host_name = $_SERVER['HTTP_HOST'];
    // [ need to cater for `host:port` since some "buggy" SAPI(s) have been known to return the port too, see http://goo.gl/bFrbCO
    $strpos = strpos($host_name, ':');
    if($strpos !== false){
        $host_name = substr($host_name, $strpos);
    }
    // ]
    // [ for dynamic verification, replace this chunk with db/file/curl queries
    $reject_request = !array_key_exists($host_name, array(
        'a.com' => null,
        'a.a.com' => null,
        'b.com' => null,
        'b.b.com' => null
    ));
    // ]
}
if($reject_request){
    // log errors
    // display errors (optional)
    exit;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
echo 'Hello World!';
// ...

The advantage of $_SERVER['HTTP_HOST'] is that its behavior is more well-defined than $_SERVER['SERVER_NAME']. Contrast ➫➫:

Contents of the Host: header from the current request, if there is one.

with:

The name of the server host under which the current script is executing.

Using a better defined interface like $_SERVER['HTTP_HOST'] means that more SAPIs will implement it using reliable well-defined behavior. (Unlike the other.) However, it is still totally SAPI dependent ➫➫:

There is no guarantee that every web server will provide any of these [$_SERVER entries]; servers may omit some, or provide others not listed here.

To understand how to properly retrieve the host name, first and foremost you need to understand that a server which contains only code has no means of knowing (pre-requisite for verifying) its own name on the network. It needs to interface with a component that supplies it its own name. This can be done via:

  • local config file

  • local database

  • hardcoded source code

  • external request (curl)

  • client/attacker's Host: request

  • etc

Usually its done via the local (SAPI) config file. Note that you have configured it correctly, e.g. in Apache ➫➫:

A couple of things need to be 'faked' to make the dynamic virtual host look like a normal one.

The most important is the server name which is used by Apache to generate self-referential URLs, etc. It is configured with the ServerName directive, and it is available to CGIs via the SERVER_NAME environment variable.

The actual value used at run time is controlled by the UseCanonicalName setting.

With UseCanonicalName Off the server name comes from the contents of the Host: header in the request. With UseCanonicalName DNS it comes from a reverse DNS lookup of the virtual host's IP address. The former setting is used for name-based dynamic virtual hosting, and the latter is used for** IP-based hosting.

If Apache cannot work out the server name because there is no Host: header or the DNS lookup fails then the value configured with ServerName is used instead.

吻安 2024-08-11 23:49:10

两者之间的主要区别在于,$_SERVER['SERVER_NAME'] 是服务器控制的变量,而 $_SERVER['HTTP_HOST'] 是用户控制的值。

经验法则是永远不要信任用户的值,因此 $_SERVER['SERVER_NAME'] 是更好的选择。

正如 Gumbo 指出的那样,如果您未设置 UseCanonicalName On,Apache 将根据用户提供的值构造 SERVER_NAME。

编辑:话虽如此,如果站点使用基于名称的虚拟主机,则 HTTP 主机标头是访问非默认站点的唯一方法。

The major difference between the two is that $_SERVER['SERVER_NAME'] is a server controlled variable, while $_SERVER['HTTP_HOST'] is a user-controlled value.

The rule of thumb is to never trust values from the user, so $_SERVER['SERVER_NAME'] is the better choice.

As Gumbo pointed out, Apache will construct SERVER_NAME from user-supplied values if you don't set UseCanonicalName On.

Edit: Having said all that, if the site is using a name-based virtual host, the HTTP Host header is the only way to reach sites that aren't the default site.

简美 2024-08-11 23:49:10

我不确定也不太信任 $_SERVER['HTTP_HOST'] 因为它依赖于来自客户端的标头。换句话说,如果客户请求的域不是我的域,他们将不会进入我的网站,因为 DNS 和 TCP/IP 协议将其指向正确的目的地。但是我不知道是否可以劫持 DNS、网络甚至 Apache 服务器。为了安全起见,我在环境中定义主机名并将其与 $_SERVER['HTTP_HOST'] 进行比较。

在根目录下的 .htaccess 文件中添加 SetEnv MyHost domain.com 并在 Common.php 中添加此代码

if (getenv('MyHost')!=$_SERVER['HTTP_HOST']) {
  header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
  exit();
}

我在每个 php 页面中都包含此 Common.php 文件。该页面执行每个请求所需的任何操作,例如 session_start()、修改会话 cookie 以及如果 post 方法来自不同域则拒绝。

I am not sure and not really trust $_SERVER['HTTP_HOST'] because it depend on header from client. In another way, if a domain requested by client is not mine one, they will not getting into my site because DNS and TCP/IP protocol point it to the correct destination. However I don't know if possible to hijack the DNS, network or even Apache server. To be safe, I define host name in environment and compare it with $_SERVER['HTTP_HOST'].

Add SetEnv MyHost domain.com in .htaccess file on root and add ths code in Common.php

if (getenv('MyHost')!=$_SERVER['HTTP_HOST']) {
  header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
  exit();
}

I include this Common.php file in every php page. This page doing anything required for each request like session_start(), modify session cookie and reject if post method come from different domain.

北城挽邺 2024-08-11 23:49:10

即使您使用 $_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME']$,XSS 也始终存在_SERVER['PHP_SELF']

XSS will always be there even if you use $_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME'] OR $_SERVER['PHP_SELF']

寄居人 2024-08-11 23:49:10

首先,我要感谢您所有好的答案和解释。
这是我根据您获取基本网址的所有答案创建的方法。我只在非常罕见的情况下使用它。因此,安全问题(例如 XSS 攻击)并没有受到太多关注。也许有人需要它。

// Get base url
function getBaseUrl($array=false) {
    $protocol = "";
    $host = "";
    $port = "";
    $dir = "";  

    // Get protocol
    if(array_key_exists("HTTPS", $_SERVER) && $_SERVER["HTTPS"] != "") {
        if($_SERVER["HTTPS"] == "on") { $protocol = "https"; }
        else { $protocol = "http"; }
    } elseif(array_key_exists("REQUEST_SCHEME", $_SERVER) && $_SERVER["REQUEST_SCHEME"] != "") { $protocol = $_SERVER["REQUEST_SCHEME"]; }

    // Get host
    if(array_key_exists("HTTP_X_FORWARDED_HOST", $_SERVER) && $_SERVER["HTTP_X_FORWARDED_HOST"] != "") { $host = trim(end(explode(',', $_SERVER["HTTP_X_FORWARDED_HOST"]))); }
    elseif(array_key_exists("SERVER_NAME", $_SERVER) && $_SERVER["SERVER_NAME"] != "") { $host = $_SERVER["SERVER_NAME"]; }
    elseif(array_key_exists("HTTP_HOST", $_SERVER) && $_SERVER["HTTP_HOST"] != "") { $host = $_SERVER["HTTP_HOST"]; }
    elseif(array_key_exists("SERVER_ADDR", $_SERVER) && $_SERVER["SERVER_ADDR"] != "") { $host = $_SERVER["SERVER_ADDR"]; }
    //elseif(array_key_exists("SSL_TLS_SNI", $_SERVER) && $_SERVER["SSL_TLS_SNI"] != "") { $host = $_SERVER["SSL_TLS_SNI"]; }

    // Get port
    if(array_key_exists("SERVER_PORT", $_SERVER) && $_SERVER["SERVER_PORT"] != "") { $port = $_SERVER["SERVER_PORT"]; }
    elseif(stripos($host, ":") !== false) { $port = substr($host, (stripos($host, ":")+1)); }
    // Remove port from host
    $host = preg_replace("/:\d+$/", "", $host);

    // Get dir
    if(array_key_exists("SCRIPT_NAME", $_SERVER) && $_SERVER["SCRIPT_NAME"] != "") { $dir = $_SERVER["SCRIPT_NAME"]; }
    elseif(array_key_exists("PHP_SELF", $_SERVER) && $_SERVER["PHP_SELF"] != "") { $dir = $_SERVER["PHP_SELF"]; }
    elseif(array_key_exists("REQUEST_URI", $_SERVER) && $_SERVER["REQUEST_URI"] != "") { $dir = $_SERVER["REQUEST_URI"]; }
    // Shorten to main dir
    if(stripos($dir, "/") !== false) { $dir = substr($dir, 0, (strripos($dir, "/")+1)); }

    // Create return value
    if(!$array) {
        if($port == "80" || $port == "443" || $port == "") { $port = ""; }
        else { $port = ":".$port; } 
        return htmlspecialchars($protocol."://".$host.$port.$dir, ENT_QUOTES); 
    } else { return ["protocol" => $protocol, "host" => $host, "port" => $port, "dir" => $dir]; }
}

First I want to thank you for all the good answers and explanations.
This is the method I created based upon all your answer to get the base url. I only use it in very rare situations. So there is NOT a big focus on security issues, like XSS attacks. Maybe someone needs it.

// Get base url
function getBaseUrl($array=false) {
    $protocol = "";
    $host = "";
    $port = "";
    $dir = "";  

    // Get protocol
    if(array_key_exists("HTTPS", $_SERVER) && $_SERVER["HTTPS"] != "") {
        if($_SERVER["HTTPS"] == "on") { $protocol = "https"; }
        else { $protocol = "http"; }
    } elseif(array_key_exists("REQUEST_SCHEME", $_SERVER) && $_SERVER["REQUEST_SCHEME"] != "") { $protocol = $_SERVER["REQUEST_SCHEME"]; }

    // Get host
    if(array_key_exists("HTTP_X_FORWARDED_HOST", $_SERVER) && $_SERVER["HTTP_X_FORWARDED_HOST"] != "") { $host = trim(end(explode(',', $_SERVER["HTTP_X_FORWARDED_HOST"]))); }
    elseif(array_key_exists("SERVER_NAME", $_SERVER) && $_SERVER["SERVER_NAME"] != "") { $host = $_SERVER["SERVER_NAME"]; }
    elseif(array_key_exists("HTTP_HOST", $_SERVER) && $_SERVER["HTTP_HOST"] != "") { $host = $_SERVER["HTTP_HOST"]; }
    elseif(array_key_exists("SERVER_ADDR", $_SERVER) && $_SERVER["SERVER_ADDR"] != "") { $host = $_SERVER["SERVER_ADDR"]; }
    //elseif(array_key_exists("SSL_TLS_SNI", $_SERVER) && $_SERVER["SSL_TLS_SNI"] != "") { $host = $_SERVER["SSL_TLS_SNI"]; }

    // Get port
    if(array_key_exists("SERVER_PORT", $_SERVER) && $_SERVER["SERVER_PORT"] != "") { $port = $_SERVER["SERVER_PORT"]; }
    elseif(stripos($host, ":") !== false) { $port = substr($host, (stripos($host, ":")+1)); }
    // Remove port from host
    $host = preg_replace("/:\d+$/", "", $host);

    // Get dir
    if(array_key_exists("SCRIPT_NAME", $_SERVER) && $_SERVER["SCRIPT_NAME"] != "") { $dir = $_SERVER["SCRIPT_NAME"]; }
    elseif(array_key_exists("PHP_SELF", $_SERVER) && $_SERVER["PHP_SELF"] != "") { $dir = $_SERVER["PHP_SELF"]; }
    elseif(array_key_exists("REQUEST_URI", $_SERVER) && $_SERVER["REQUEST_URI"] != "") { $dir = $_SERVER["REQUEST_URI"]; }
    // Shorten to main dir
    if(stripos($dir, "/") !== false) { $dir = substr($dir, 0, (strripos($dir, "/")+1)); }

    // Create return value
    if(!$array) {
        if($port == "80" || $port == "443" || $port == "") { $port = ""; }
        else { $port = ":".$port; } 
        return htmlspecialchars($protocol."://".$host.$port.$dir, ENT_QUOTES); 
    } else { return ["protocol" => $protocol, "host" => $host, "port" => $port, "dir" => $dir]; }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文