Obter o URL completo em PHP: como não reinventar a roda e não perder performance
\n\nMais cedo ou mais tarde, todo desenvolvedor PHP se depara com a pergunta: "Como faço para obter o URL completo da página atual?". Parece simples — pegue o $_SERVER['REQUEST_URI'], adicione o domínio e pronto. Mas o diabo, como sempre, está nos detalhes. A pergunta "Get the full URL in PHP" no Stack Overflow acumulou milhares de visualizações e dezenas de respostas. E não é à toa.
Neste artigo, vamos explorar como obter o URL completo em PHP de forma correta, rápida e sem bugs. Você vai descobrir a diferença entre HTTP_HOST e SERVER_NAME, como determinar o protocolo (HTTPS ou HTTP) corretamente e por que não deve copiar código da internet cegamente. Vamos nessa!
Por que o jeito mais simples nem sempre é o melhor?
\n\nA solução mais óbvia seria algo assim:
\n\n<?php\n$urlCompleta = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];\n?>\n\nEsse código funciona, mas apenas se você tiver:
\n- \n
- Obrigatoriamente HTTP (não HTTPS) \n
- Nenhuma porta diferente de 80 ou 443 \n
- O servidor transmitindo corretamente o cabeçalho Host \n
No mundo real, essas condições nem sempre são atendidas. Se o site roda em HTTPS, você obterá uma URL com http://, causando erros de conteúdo misto no navegador. Se a porta não for padrão (ex.: 8080), ela será perdida. E se um invasor falsificar o cabeçalho Host, você pode acabar com uma URL apontando para um domínio diferente.
\n\nComponentes do URL completo: entendendo cada parte
\n\nAntes de escrever uma solução universal, vamos lembrar do que um URL completo é composto:
\n\nesquema://host:porta/caminho?consulta#fragmento\n\nNo PHP, temos acesso aos seguintes elementos do array superglobal $_SERVER:
- \n
- esquema — protocolo (HTTP ou HTTPS). Não há uma chave direta, mas podemos identificá-lo através de
$_SERVER['HTTPS']ou$_SERVER['REQUEST_SCHEME'](disponível desde o PHP 7.4). \n - host — domínio. Temos dois candidatos:
HTTP_HOST(do cabeçalho Host) eSERVER_NAME(da configuração do servidor). \n - porta — porta.
$_SERVER['SERVER_PORT']. Se a porta for padrão (80 para HTTP, 443 para HTTPS), podemos omiti-la. \n - caminho — caminho.
$_SERVER['REQUEST_URI']contém o caminho e a string de consulta. \n - consulta — string de consulta. Pode ser obtida separadamente com
$_SERVER['QUERY_STRING']. \n - fragmento — âncora. Não é enviada ao servidor, portanto não é possível obtê-la. \n
Solução correta: a função getFullUrl()
\n\nAqui está uma maneira confiável e eficiente de obter o URL completo em PHP:
\n\n<?php\nfunction getFullUrl(): string {\n // Determina o protocolo\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: prefere HTTP_HOST, mas verifica se não está vazio\n $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';\n\n // Porta: adiciona apenas se não for padrão\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\nEsta função leva em consideração:
\n- \n
- Protocolo HTTPS (inclusive através de servidores proxy com o cabeçalho
X-Forwarded-Proto). \n - Portas não padrão. \n