Obtener la URL completa en PHP: cómo no reinventar la rueda y no perder rendimiento
\n\nTodo desarrollador PHP, tarde o temprano, se enfrenta a la pregunta: «¿Cómo obtengo la URL completa de la página actual?». Parece sencillo: tomas $_SERVER['REQUEST_URI'], le agregas el dominio y listo. Pero el diablo, como siempre, está en los detalles. La pregunta «Obtener la URL completa en PHP» en Stack Overflow ha acumulado miles de visitas y decenas de respuestas. Y no es casualidad.
En este artículo analizaremos cómo obtener la URL completa en PHP de forma correcta, rápida y sin errores. Descubrirás la diferencia entre HTTP_HOST y SERVER_NAME, cómo determinar correctamente el protocolo (HTTPS o HTTP), y por qué no debes copiar código ciegamente de internet. ¡Allá vamos!
¿Por qué la forma simple no siempre es la mejor?
\n\nLa opción más obvia se ve así:
\n\n<?php\n$fullUrl = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];\n?>\n\nEste código funciona, pero solo si tienes:
\n- \n
- Obligatoriamente HTTP (no HTTPS) \n
- Un puerto estándar (80 o 443) \n
- Un servidor que transmita correctamente la cabecera Host \n
En el mundo real, estas condiciones no siempre se cumplen. Si el sitio funciona con HTTPS, obtendrás una URL con http://, lo que generará un error de contenido mixto en el navegador. Si el puerto no es estándar (por ejemplo, 8080), se perderá. Y si un atacante falsifica la cabecera Host, podrías obtener una URL de un dominio ajeno.
\n\nComponentes de la URL completa: análisis detallado
\n\nAntes de escribir una solución universal, recordemos de qué se compone una URL completa:
\n\nscheme://host:port/path?query#fragment\n\nEn PHP, tenemos acceso a los siguientes elementos del array superglobal $_SERVER:
- \n
- scheme — protocolo (HTTP o HTTPS). No hay una clave directa, pero se puede determinar mediante
$_SERVER['HTTPS']o$_SERVER['REQUEST_SCHEME'](disponible desde PHP 7.4). \n - host — dominio. Hay dos candidatos:
HTTP_HOST(de la cabecera Host) ySERVER_NAME(de la configuración del servidor). \n - port — puerto.
$_SERVER['SERVER_PORT']. Si el puerto es estándar (80 para HTTP, 443 para HTTPS), se puede omitir. \n - path — ruta.
$_SERVER['REQUEST_URI']contiene la ruta y la cadena de consulta. \n - query — cadena de consulta. Se puede obtener por separado con
$_SERVER['QUERY_STRING']. \n - fragment — ancla. No se envía al servidor, por lo que no se puede obtener. \n
Solución correcta: la función getFullUrl()
\n\nAquí tienes una forma fiable y eficiente de obtener la URL completa en PHP:
\n\n<?php\nfunction getFullUrl(): string {\n // Determinamos el 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: preferimos HTTP_HOST, pero verificamos que no esté vacío\n $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';\n\n // Puerto: solo lo agregamos si no es estándar\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 función tiene en cuenta:
\n- \n
- El protocolo HTTPS (incluso a través de servidores proxy con la cabecera
X-Forwarded-Proto). \n - Puertos no estándar. \n