Geonode logo
Geonode Team

Geonode Team

Actualizado: 7 de octubre de 2026

Publicado: 2 de septiembre de 2026

Cómo realizar una solicitud HEAD con curl

`curl -I` envía una solicitud HEAD. Parece que `curl -X HEAD` debería hacer lo mismo, pero hace algo que falla sutilmente, lo cual es una forma estupenda de perder veinte minutos con un terminal bloqueado. El manual de curl lo explica claramente, y merece la pena comprender el motivo, ya que se aplica a todos los métodos que puedas configurar manualmente. Esta guía aborda la sintaxis correcta, las normas de la especificación sobre lo que los servidores pueden omitir y los casos en los que una solicitud HEAD te proporciona información que una GET no ofrecería.

Por qué nos importa: somos Geonode y vendemos servidores proxy, por lo que la gente utiliza constantemente solicitudes HEAD a través de nosotros para comprobar enlaces, tamaños y disponibilidad de forma económica. La advertencia sincera es que HEAD es una solicitud diferente, no un GET «ligero», y tratarla como tal lleva a conclusiones erróneas con toda seguridad. Una URL que devuelve un 200 a una solicitud HEAD puede devolver un 403 a una solicitud GET. Un recurso que no muestra ningún «Content-Length» con una solicitud HEAD puede tener uno con una solicitud GET. Y una capa antibots puede interpretar una solicitud HEAD inusual como una señal en sí misma. La solicitud HEAD es excelente para lo que está destinada; sin embargo, no es un buen indicador de «qué pasaría si realmente recuperara esto».

La forma correcta de utilizar «

curl -I https://example.com

»

El manual de curl describe «-I, --head

» de la siguiente manera: «(HTTP FTP FILE) Obtiene solo los encabezados. Los servidores HTTP disponen del comando HEAD, que esta función utiliza para obtener únicamente el encabezado de un documento. Cuando se utiliza con una URL de tipo FTP o FILE, curl muestra únicamente el tamaño del archivo y la fecha de la última modificación».

Resultado:

HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256
last-modified: Thu, 17 Oct 2019 07:18:26 GMT
cache-control: max-age=604800

Esa es toda la respuesta a la pregunta del título. Lo que viene a continuación es la parte que permite ahorrar tiempo.

Por qué «-X HEAD» es incorrecto

El manual de curl aborda este tema directamente en el apartado «-X, --request»:

Esta opción solo cambia la palabra concreta que se utiliza en la solicitud HTTP, pero no modifica el comportamiento de curl. Por ejemplo, si quieres realizar una solicitud HEAD correcta, no basta con utilizar «-X HEAD». Tienes que utilizar la opción «--head».

El mecanismo: -X cambia la cadena del método y nada más. curl sigue comportándose como si estuviera realizando una solicitud GET, lo que significa que sigue esperando un cuerpo de respuesta. El servidor, que implementa correctamente el método HEAD, envía encabezados pero no cuerpo. curl espera un contenido que nunca llegará, y el comando parece quedarse colgado hasta que un tiempo de espera o el cierre de la conexión lo interrumpe.

El manual también advierte sobre un segundo comportamiento de «-X» que confunde a los usuarios con las redirecciones: «Si se utiliza --location, la cadena de método que se establezca con --request se utilizará para todas las solicitudes». Así pues, -X POST -L reenvía un POST a cada salto de una cadena de redirecciones, lo cual rara vez es lo que se pretende.

El principio general, tal y como se indica en el propio manual: «Normalmente no necesitas esta opción. Todo tipo de peticiones GET, HEAD, POST y PUT se invocan más bien utilizando opciones específicas de la línea de comandos». Utiliza -I para HEAD, -d para POST, -T para PUT, y reserva -X para métodos realmente poco habituales, como PROPFIND.

Qué es realmente HEAD

El RFC 9110, en el apartado §9.3.2, lo define en una sola frase:

El método HEAD es idéntico al GET, salvo que el servidor NO DEBE enviar contenido en la respuesta.

Y establece su finalidad: «HEAD se utiliza para obtener metadatos sobre la representación seleccionada sin transferir los datos de dicha representación, a menudo con el fin de comprobar enlaces de hipertexto o detectar modificaciones recientes».

Se trata de un requisito estricto para los servidores —MUST NOT enviar contenido— y es la razón por la que curl se cuelga cuando se le indica que debe esperar recibirlo.

La regla relativa a los encabezados es deliberadamente más flexible, y esta es la parte que la gente suele malinterpretar:

El servidor DEBERÍA enviar en respuesta a una solicitud HEAD los mismos campos de encabezado que habría enviado si el método de solicitud hubiera sido GET. Sin embargo, un servidor PUEDE omitir los campos de encabezado cuyo valor solo se determine al generar el contenido.

El RFC ofrece un ejemplo concreto: los servidores que almacenan en búfer respuestas dinámicas pueden generar Content-Length y Vary en una solicitud GET que «no se generan dentro de una respuesta HEAD». Las denomina «inconsistencias menores» y las considera «preferibles a generar y descartar el contenido para una solicitud HEAD, ya que la solicitud HEAD suele realizarse en aras de la eficiencia».

Por lo tanto, la ausencia de Content-Length en una solicitud HEAD no es necesariamente un error ni tiene por qué ser significativa. Puede tratarse simplemente de un servidor que se niega a calcular algo que solo habría podido conocer al renderizar la página.

También hay una regla sobre los cuerpos de las solicitudes que conviene conocer si estás desarrollando herramientas. El contenido de una solicitud HEAD «no tiene una semántica definida de forma general, no puede alterar el significado ni el destino de la solicitud, y podría llevar a algunas implementaciones a rechazar la solicitud y cerrar la conexión debido a su potencial como ataque de contrabando de solicitudes». El RFC establece que un cliente «NO DEBERÍA generar contenido en una solicitud HEAD» salvo que exista un acuerdo previo específico. No envíes un cuerpo con una solicitud HEAD.

Para qué sirve HEAD

Casos realmente útiles, en los que se cambia una transferencia completa por unos pocos cientos de bytes.

Comprobar si una URL está activa:

curl -sI -o /dev/null -w '%{response_code}\n' https://example.com

Averiguar el tamaño de un archivo antes de descargarlo:

curl -sI https://example.com/large.iso | grep -i content-length

Seguir y notificar una cadena de redireccionamientos:

curl -sIL -o /dev/null -w '%{num_redirects} hops -> %{url_effective}\n' https://example.com

Comprobar si un servidor admite solicitudes de rango, lo que determina si se puede reanudar una descarga interrumpida:

curl -sI https://example.com/file.zip | grep -i accept-ranges

Comprobar la actualidad sin descargar:

curl -sI https://example.com/data.json | grep -iE 'last-modified|etag'

Comprobación masiva de enlaces, que es el uso clásico y donde el ahorro de ancho de banda se multiplica:

while read -r url; do
  code=$(curl -sIL -o /dev/null -w '%{response_code}' --max-time 10 "$url")
  echo "$code $url"
done < urls.txt

Con un ancho de banda medido, el ahorro es real: una comprobación de enlace que transferiría 500 KB por URL transfiere, en su lugar, unos pocos cientos de bytes. En diez mil URL, esa es la diferencia entre cinco gigabytes y unos pocos megabytes.

Cuando HEAD te induce a error

Los modos de fallo, que son el motivo de la advertencia del principio.

El servidor rechaza por completo la solicitud HEAD. 405 Method Not Allowed en una URL en la que GET funciona perfectamente. Poco habitual en contenido estático, pero no es raro en API y puntos finales de aplicaciones.

El servidor gestiona la solicitud HEAD de forma diferente. Códigos de estado distintos, encabezados distintos y, en ocasiones, una ruta de código completamente diferente en la aplicación. El RFC permite omitir los encabezados derivados del contenido, y las implementaciones varían en cuanto al alcance que le dan a esta posibilidad.

Las cachés y las CDN pueden indexar las solicitudes HEAD por separado. Los encabezados de caché de una solicitud HEAD pueden reflejar una entrada de caché diferente a la que encontraría una solicitud GET, por lo que una solicitud HEAD no es una forma fiable de inspeccionar el comportamiento del almacenamiento en caché.

Los sistemas antibots responden de forma diferente. Una solicitud HEAD procedente de un cliente desconocido es en sí misma una señal, y es posible que la respuesta que se obtenga no sea la misma que recibiría una solicitud GET procedente de un navegador.

El campo «Content-Length » puede estar ausente o ser incorrecto. Aunque lo permite la especificación y es habitual en el contenido dinámico, constituye una base poco fiable para estimar el tamaño de descarga de cualquier contenido generado.

Las cadenas de redireccionamiento pueden variar. Algunos servidores redirigen las peticiones GET y HEAD a destinos diferentes, especialmente cuando interviene la negociación de contenido.

Cuando necesites saber qué haría una solicitud real, realiza una solicitud real y descarta el cuerpo:

curl -sS -o /dev/null -D - https://example.com

Una solicitud GET genuina, con los encabezados en la salida estándar y el cuerpo descartado. Pagas el ancho de banda y obtienes una respuesta precisa. Elige deliberadamente entre las dos opciones: -I cuando quieras algo barato, -o /dev/null -D - cuando quieras algo preciso.

Existe una opción intermedia para recursos de gran tamaño: solicitar un byte en lugar de todo el contenido:

curl -sS -r 0-0 -o /dev/null -D - https://example.com/large.iso

-r, --range recupera «un rango de bytes (es decir, un documento parcial)», por lo que 0-0 solo obtiene el primer byte. Se trata de una solicitud GET auténtica con el comportamiento propio de una GET, sin apenas coste de ancho de banda. La advertencia del manual: «Muchos servidores HTTP/1.1 no tienen esta función habilitada», así que comprueba primero si existe Accept-Ranges: bytes y espera una respuesta completa en caso de que no exista.

Patrones que vale la pena copiar en los scripts

Los comandos anteriores resultan considerablemente más útiles si se les aplica un poco de estructura.

Un verificador de enlaces que informa con precisión. La versión más sencilla considera cualquier código de respuesta que no sea 200 como un enlace roto, lo que genera falsas alarmas en las redirecciones y en los servidores que rechazan la solicitud HEAD. Este distingue entre ambos casos:

check() {
  local url="$1" code
  code=$(curl -sIL -o /dev/null --max-time 10 -w '%{response_code}' "$url")
  case "$code" in
    200) echo "OK       $url" ;;
    405) code=$(curl -sSL -o /dev/null --max-time 10 -w '%{response_code}' "$url")
         echo "GET:$code $url" ;;
    000) echo "TIMEOUT  $url" ;;
    *)   echo "$code     $url" ;;
  esac
}

La rama «405» es importante: un servidor que rechaza HEAD no es un enlace roto, y volver a intentarlo con un GET es la única forma de saberlo. 000 es la forma que tiene curl de indicar que no se ha recibido ninguna respuesta HTTP, lo que distingue un fallo de red de un error del servidor.

Paralelismo, con cuidado. La comprobación de enlaces es vergonzosamente paralela y la tentación es ejecutarla a lo grande. Resiste:

xargs -P 8 -I{} sh -c 'check "$1"' _ {} < urls.txt

Ocho es un valor predeterminado razonable. El límite máximo aquí es la cortesía de no saturar el servidor de otra persona, más que tu propia capacidad, y un comprobador de enlaces que genere un incidente de limitación de tasa ha costado más de lo que ha ahorrado.

Establece siempre un tiempo de espera. Una solicitud HEAD a un host que no responde se queda colgada exactamente el mismo tiempo que lo haría una solicitud GET. --max-time 10 con --connect-timeout 5 establece ese límite, y en un bucle con miles de URL, ese límite es lo que hace que la tarea termine.

Registra la URL efectiva, no solo el estado. %{url_effective} tras -L te indica dónde condujo realmente un enlace, lo que convierte un «este enlace funciona» en un «este enlace funciona y ahora apunta a otro sitio» —por lo general, el hallazgo más interesante—.

Almacena los resultados en caché. Volver a comprobar cada URL en cada ejecución desperdicia ancho de banda y buena voluntad. Almacena el estado y el ETag o Last-Modified, y utiliza solicitudes condicionales en pasadas posteriores para que los recursos que no hayan cambiado solo requieran un 304 en lugar de una comprobación completa.

HEAD a través de un proxy

Hay tres cambios que conviene conocer.

curl -I -x http://user:pass@proxy.example.com:9000 https://example.com

La respuesta «CONNECT» aparece primero en el caso de HTTPS. curl establece un túnel antes de la solicitud HEAD y, en la salida detallada, la respuesta del proxy a dicha solicitud aparece antes que la del destino. --suppress-connect-headers la oculta; %{http_connect} muestra el estado del proxy por separado del del destino.

La clave está en el ahorro de ancho de banda. En el tráfico medido, una solicitud HEAD consume unos pocos cientos de bytes frente a los que ocupa una página completa. Para la validación de enlaces, la supervisión de la disponibilidad y las comprobaciones de tamaño a gran escala, esta es la diferencia entre un trabajo asequible y uno costoso. Es una de las pocas optimizaciones realmente importantes disponibles en la tarificación por gigabyte.

Pero los bloqueos y los retos se comportan de forma diferente. Una capa antibots que sirva una página de reto a una solicitud GET puede simplemente rechazar una solicitud HEAD, o viceversa. Si utilizas HEAD para comprobar si un destino es accesible, verifica el resultado con una solicitud GET real en una muestra antes de confiar en él para toda una lista. Este es el patrón de «fallo silencioso» sobre el que escribimos en por qué es importante probar los proxies: la solicitud se realiza con éxito, la respuesta es incorrecta y nada te lo indica.

Un detalle de curl que conviene conocer: -G, --get se combina con --head. El manual señala que, cuando se utiliza -G junto con --head, «los datos POST se añaden a la URL con una solicitud HEAD», lo cual resulta útil cuando necesitas parámetros de consulta formados por pares clave-valor en una solicitud HEAD.

Cómo interpretar correctamente la respuesta

Sacar el máximo partido a la información recibida.

Lo primero es el estado. «200 » significa que existe. «301 » / «302 » significa que se ha trasladado; añade «-L » para seguir el rastro. «403 » significa que se ha rechazado. «404 » significa que ya no existe. «405 » significa que el servidor no acepta HEAD, así que vuelve a intentarlo con un GET. «429 » significa que hay que reducir la velocidad.

**Content-Length ** si está presente, teniendo en cuenta que la especificación permite omitirlo.

**Accept-Ranges: bytes ** significa que están disponibles las descargas reanudables y las solicitudes de rango.

**Last-Modified y ETag ** permiten realizar solicitudes condicionales. -z envía If-Modified-Since —el manual lo describe como solicitar «un archivo que haya sido modificado después de la fecha y hora indicadas»— y --etag-compare se encarga de la parte ETag . Un 304 Not Modified no cuesta prácticamente nada y es la forma correcta de consultar un recurso repetidamente.

**Content-Type ** te indica lo que habrías recibido. text/html , donde esperabas JSON, suele significar un error o una página de inicio de sesión.

Para uso automático, omite por completo el análisis del texto:

curl -sI -o /dev/null -w '%{header_json}' https://example.com | jq

%{header_json} devuelve todos los encabezados de respuesta como JSON con nombres en minúsculas y valores en forma de matriz, lo que gestiona correctamente los encabezados repetidos y elimina la necesidad de un analizador. Hemos tratado este tema y otras opciones de inspección en cómo mostrar los encabezados de respuesta con curl.

Preguntas frecuentes

¿Cómo envío una solicitud HEAD con curl?

curl -I https://example.com. La sintaxis completa es --head. No utilices -X HEAD: el manual indica explícitamente que «no es suficiente» para una solicitud HEAD correcta, ya que solo cambia la cadena del método, mientras que curl sigue esperando un cuerpo de respuesta.

¿Por qué se cuelga «curl -X HEAD»?

Porque «-X» solo cambia la palabra en la línea de solicitud, no el comportamiento de curl. curl sigue esperando un cuerpo de respuesta, mientras que el servidor, correctamente, no envía ninguno, ya que la RFC 9110 exige que un servidor «NO DEBE enviar contenido» en una respuesta HEAD. Utiliza en su lugar «-I».

¿Cuál es la diferencia entre HEAD y GET?

HEAD es idéntico a GET, salvo que el servidor no debe enviar el cuerpo. Su finalidad es obtener metadatos sin transferir contenido, normalmente para comprobar enlaces o verificar la actualidad de la información. Los servidores deben enviar los mismos encabezados que enviarían para GET, pero pueden omitir aquellos que solo se calculan al generar el contenido.

¿Devuelve HEAD siempre los mismos encabezados que GET?

No. La especificación indica que los servidores DEBERÍAN enviar los mismos encabezados, pero PUEDEN omitir aquellos «cuyo valor se determina únicamente durante la generación del contenido»; cita como ejemplos Content-Length y Vary. Considera que estas pequeñas inconsistencias son preferibles a generar y descartar un cuerpo.

¿Cómo puedo obtener el tamaño de un archivo sin descargarlo?

curl -sI URL | grep -i content-length. Ten en cuenta que el encabezado puede estar ausente en el caso de contenido generado dinámicamente, lo cual permite la especificación. Para obtener una respuesta más fiable y prácticamente sin coste alguno, solicita un solo byte con -r 0-0 y lee el encabezado «Content-Range».

¿Por qué una URL funciona en un navegador pero devuelve un 405 al usar curl -I?

Porque el servidor no acepta HEAD en ese punto final. 405 Method Not Allowed es una respuesta válida a una solicitud HEAD por parte de un servidor que gestiona correctamente las solicitudes GET. Vuelve a intentarlo con una solicitud GET sin cuerpo: curl -sS -o /dev/null -D - URL.

¿Puedo enviar una solicitud HEAD con cuerpo?

No deberías hacerlo. La RFC 9110 establece que el contenido de una solicitud HEAD «no tiene una semántica definida de forma general», no puede alterar el significado de la solicitud y «podría llevar a algunas implementaciones a rechazar la solicitud y cerrar la conexión debido a su potencial como ataque de contrabando de solicitudes». Los clientes NO DEBERÍAN generar contenido en una solicitud HEAD.

¿Es útil la solicitud HEAD para comprobar si un proxy funciona?

En parte. Confirma la conectividad y devuelve el código de estado de forma sencilla, lo que constituye una buena prueba de funcionamiento básico. No indica si una solicitud GET real tendría éxito, ya que las capas antibots suelen tratar ambas de forma diferente. Valida los resultados con solicitudes GET reales en una muestra antes de confiar en los resultados de HEAD para toda una lista.

Conclusión

Dos comandos lo cubren todo. curl -I URL para una solicitud HEAD correcta, y curl -sS -o /dev/null -D - URL cuando quieras los encabezados que produciría una solicitud GET real. Lo que no funciona es -X HEAD, y el manual lo deja claro: cambia la palabra, pero no el comportamiento, por lo que curl espera un cuerpo que el servidor no debe enviar.

La decisión depende de cuál de las dos opciones prefieras. HEAD es mucho más ligero —unos pocos cientos de bytes frente a una página completa—, lo que la convierte en la herramienta adecuada para comprobar enlaces, supervisar la disponibilidad y estimar el tamaño a cualquier volumen, y especialmente con ancho de banda medido. Sin embargo, no es la herramienta adecuada para predecir qué devolvería una solicitud real, ya que los servidores pueden omitir los encabezados derivados del contenido, pueden rechazar directamente la solicitud HEAD y, con frecuencia, la procesan mediante una lógica diferente.

Y si necesitas la precisión de un GET sin consumir ancho de banda, -r 0-0 es la solución intermedia poco utilizada: un auténtico GET que recupera un solo byte. Comprueba primero Accept-Ranges: bytes, ya que muchos servidores te entregarán el archivo completo de todos modos.