Publicado originalmente en agosto de 2021. Actualizado en mayo de 2025.
Respuesta rápida: para usar curl, abre una terminal y escribe curl seguido de la URL que quieras solicitar; por ejemplo, curl https://www.fastly.com/. Esto envía una solicitud HTTP e imprime la respuesta directamente en la pantalla.
Conclusiones principales:
Usa
curl -svo /dev/nullpara inspeccionar los detalles de la conexión, las negociaciones de TLS, los encabezados de solicitud y los encabezados de respuesta sin el ruido de un cuerpo de respuesta.Usa
-H "host: ..."para probar cómo responde un servidor a distintos encabezados de host.Usa
--resolvepara anular el DNS y enviar solicitudes a una dirección IP específica.Usa
--connect-topara redireccionar conexiones y conservar el nombre del host de la solicitud original.La combinación de estas opciones facilita la solución de problemas de enrutamiento, DNS, TLS y servidores de origen.
¿Alguna vez has tenido una de esas semanas en las que todo parece seguir el mismo patrón? Como ingeniero de ventas, trabajo con nuestros clientes para crear y desplegar sistemas, y la semana pasada tuvo un claro patrón.
Me llegó lo que parecía en principio un caso de uso único, en el que usé una herramienta concreta (cURL) de una manera particular para probar cómo respondía un servidor de origen. Este procedimiento te puede servir, por ejemplo, si quieres hacer pruebas cuando una aplicación responde con mensajes de error raros. Bueno, ¡pues no paraban de llegar casos de ese tipo! Expliqué el funcionamiento a varios compañeros y a unos cuantos clientes que intentaban resolver problemas con sus despliegues, y pensé que estaría bien hacer una publicación en el blog para todo el mundo. Empecemos.
Un breve contexto sobre cURL
Client URL (también llamado cURL o curl) fue lanzado en 1997 por Daniel Stenberg, que se ha mantenido al frente del proyecto desde entonces. En su momento se creó para automatizar la obtención del tipo de cambio de divisas para usuarios de IRC, pero en la actualidad se usa para todo tipo de recuperaciones de URL. Desde su lanzamiento, Daniel ha seguido desarrollando y añadiendo funcionalidades al proyecto a medida que se convertía en una pieza básica de otros proyectos. Es todo un héroe.
Curl es una utilidad de línea de comandos que permite enviar solicitudes HTTP a una URL y recibir el resultado. De forma predeterminada, curl envía una solicitud HTTP GET, algo que resulta muy útil para probar sitios web, API y servidores de origen. Viene por defecto en sistemas operativos como macOS y muchas distribuciones de Linux. Como la gran mayoría de Internet gira en torno a HTTP, es una herramienta ideal para rebuscar en páginas web, API o cualquier otro elemento con una interfaz HTTP.
En nuestra demostración, utilizaremos curl para simular la experiencia de solicitar una página web desde el navegador. Así podemos tener un control total de la solicitud y resolver problemas con mucha más facilidad.
A continuación tenemos un comando curl sencillo que ejecuté desde la aplicación Terminal de mi MacBook. Este comando solicita la página de inicio de fastly.com y muestra el HTML completo. Atención, porque este resultado incluye mucho ruido que ya limpiaremos luego.
$ curl https://www.fastly.com/Indicadores habituales de cURL para la resolución de problemas
-s: modo silencioso-v: salida detallada que muestra los datos de la solicitud y la respuesta-o: escribir la salida en un archivo-I: devolver solo los encabezados de respuesta-H: añadir un encabezado HTTP personalizado--resolve: anular la resolución de DNS para un nombre de host--connect-to: conectarse a un host o una IP diferentes
Curl abre muchas posibilidades al poder manipularse la solicitud mediante marcas. Por ejemplo, enviar -I limita la respuesta a solamente los encabezados del servidor remoto, sin el contenido.
Las marcas que uso en casi todos los curl son -svo, que significan modo silencioso (s), modo detallado (v) y escribiendo en un archivo (o), que escribo en dev/null.
El comando curl completo es curl -svo /dev/null https://www.fastly.com. Así, puedo centrarme en la solicitud detallada sin las distracciones de un cuerpo de respuesta lleno de ruido. Los encabezados tienen una pequeña marca que indica si se han enviado o recibido, y la negociación SSL se muestra antes de la solicitud principal.
A continuación puedes ver el comando tal como está escrito ($), la conexión y la negociación SSL (*), la petición (>) y la respuesta (<).
$ curl -svo /dev/null https://www.fastly.com/
* Trying 199.232.77.57...
* TCP_NODELAY set
* Connected to www.fastly.com (199.232.77.57) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [228 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [102 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2828 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=US; ST=California; L=San Francisco; O=Fastly, Inc.; CN=www.fastly.com
* start date: Mar 3 21:56:03 2021 GMT
* expire date: Apr 4 21:56:03 2022 GMT
* subjectAltName: host "www.fastly.com" matched cert's "www.fastly.com"
* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fe1f980aa00)
> GET / HTTP/2
> Host: www.fastly.com
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< alt-svc: h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400
< etag: "5c770df920f8c90e4c4532c32aea6ec3"
< content-type: text/html
< accept-ranges: bytes
< date: Mon, 09 Aug 2021 15:38:35 GMT
< x-served-by: cache-pwk4963-PWK
< x-cache: HIT
< x-cache-hits: 2
< x-timer: S1628523515.208976,VS0,VE0
< vary: Accept-Encoding
< x-xss-protection: 1; mode=block
< x-frame-options: DENY
< x-content-type-options: nosniff
< cache-control: max-age=0, private, must-revalidate
< server: Artisanal bits
< strict-transport-security: max-age=31536000
< content-length: 777219
<
{ [1113 bytes data]
* Connection #0 to host www.fastly.com left intact
* Closing connection 0Cómo utiliza cURL la URL y el nombre SSL
En el ejemplo anterior, el nombre del certificado SSL, el encabezado del host y el nombre del DNS tienen el mismo valor: www.fastly.com. A veces, eso es todo lo que necesitas. A partir de aquí, sin embargo, separaremos estos elementos para repasar aspectos de distintos objetivos de resolución de problemas.
El uso actual de https://www.fastly.com/ muestra la URL que se solicita. El nombre de host que se utilice dentro de la URL (p. ej., www.fastly.com) será el valor que curl utilice para solicitar y verificar el nombre del certificado SSL. Esto es importante para validar que TLS funciona como esperas y que tu sitio está protegido por el certificado previsto. Veamos el siguiente ejemplo:
$ curl -svo /dev/null https://www.fastly.com/
* Trying 199.232.77.57...
* TCP_NODELAY set
* Connected to www.fastly.com (199.232.77.57) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [228 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [102 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2828 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=US; ST=California; L=San Francisco; O=Fastly, Inc.; CN=www.fastly.com
* start date: Mar 3 21:56:03 2021 GMT
* expire date: Apr 4 21:56:03 2022 GMT
* subjectAltName: host "www.fastly.com" matched cert's "www.fastly.com"
* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018
* SSL certificate verify ok.
--->{Truncated for readability}<---
Cómo especificar una ruta en cURL
Los otros dos elementos que permanecen de la URL original son el esquema, como HTTP o HTTPS, y la ruta hasta el recurso concreto que se solicita. Me gusta pensar en la URL como el objetivo final, mientras que todo lo demás es simplemente cómo o dónde la estamos pidiendo.
La URL también define el esquema (como HTTP frente a HTTPS) y la ruta hasta el recurso que se solicita.
Para especificar una ruta en curl, inclúyela directamente en la URL: curl https://www.fastly.com/blog/
Esto solicita el recurso `/blog/` y mantiene el mismo host, la validación de certificados y el comportamiento de enrutamiento.
Cómo configurar encabezados Host en cURL
Los encabezados HTTP permiten a los clientes enviar información adicional junto con una solicitud. En curl, los encabezados se pueden añadir usando la opción `-H` o `--header`. Uno de los encabezados más útiles para solucionar problemas es el encabezado Host, que le indica al servidor web qué nombre de host estás solicitando. Un servidor web normal puede alojar varios sitios con diferentes nombres de dominio, como blog.example.com o docs.example.com. Aunque los sitios puedan encontrarse en el mismo sistema, el código fuente o la ruta de la URL podrían ser diferentes.
Si queremos cambiar el encabezado del host, podemos enviarlo definiendo explícitamente el encabezado dentro de curl. Los encabezados se pueden declarar con un --header o, para abreviar, con la marca -H. Luego, el valor se encapsula con comillas y se define el nombre del encabezado. El comando curl queda así:
$ curl -svo /dev/null https://www.fastly.com/ -H “host: blog.fastly.com”Esta solicitud pregunta por el host de blog.fastly.com, usando el certificado y la ubicación de www.fastly.com. Esto resulta muy útil si quieres comprobar la diferencia entre la raíz de un dominio, como entre fastly.com y www.fastly.com. Aquí vemos que proporciona correctamente un 301 a www.
$ curl -svo /dev/null https://www.fastly.com/ -H "host: fastly.com"
* Trying 199.232.77.57...
* TCP_NODELAY set
* Connected to www.fastly.com (199.232.77.57) port 443 (#0)
--->{Truncated for readability}<---
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fa15480aa00)
> GET / HTTP/2
> Host: fastly.com
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 301
< retry-after: 0
< accept-ranges: bytes
< date: Mon, 09 Aug 2021 16:26:07 GMT
< x-served-by: cache-pwk4938-PWK
< x-cache: HIT
< x-cache-hits: 0
< cache-control: max-age=0, private, must-revalidate
< server: Artisanal bits
< strict-transport-security: max-age=31536000
< location: https://www.fastly.com/
< content-length: 0
<
{ [0 bytes data]
* Connection #0 to host www.fastly.com left intact
* Closing connection 0
Cómo usar cURL --resolve para anular DNS
De todas las opciones de curl, --resolve es probablemente la más subestimada. Esta técnica resulta especialmente útil para solucionar problemas relacionados con el enrutamiento de peticiones. Al anular la resolución de DNS, puedes comprobar cómo responde un servidor de origen concreto antes de actualizar el DNS público, verificar un nuevo despliegue o aislar problemas dentro de una cadena de proxies y balanceo de carga. Fíjate que al principio del todo de cada curl que hemos mostrado aparece Trying X.X.X.X como dirección IP, resolviendo el nombre del dominio desde el DNS. Sin embargo, hay ocasiones en las que el nombre del DNS no es el objetivo real al que te gustaría llegar. Puede que busques algún despliegue inicial y estés probando que el servicio se resuelva bien antes de cambiar el DNS para difundir dicho cambio, o tal vez hay una cadena de proxis inversos, donde el DNS público solo se resuelve al principio de la cadena, pero queremos saber cómo responde el origen en sí.
Para eludir estas dificultades, podemos resolver nosotros mismos el nombre de dominio de curl y proporcionar la dirección IP que queramos. Para ello, tenemos que seguir un proceso de dos pasos:
A menos que ya conozcas la IP a la que quieres dirigirte, puede que sea necesario realizar una resolución de DNS del host para captar la IP. Me gusta usar Dig, otra utilidad estándar que realiza esa consulta de DNS desde la línea de comandos.
$ dig www.fastly.com +short
prod.www-fastly-com.map.fastly.net.
151.101.185.572. A continuación, podemos asignar esta IP para que la use curl al realizar la solicitud HTTP usando la opción --resolve, junto con el nombre de dominio que se reemplazará, el puerto y la IP que se utilizará. Todo junto queda así:
$ curl -svo /dev/null https://www.fastly.com/ -H "host: fastly.com" --resolve www.fastly.com:443:151.101.185.57
* Added www.fastly.com:443:151.101.185.57 to DNS cache
* Hostname www.fastly.com was found in DNS cache
* Trying 151.101.185.57...
* TCP_NODELAY set
* Connected to www.fastly.com (151.101.185.57) port 443 (#0)
--->{Truncated for readability}<---
La alternativa connect-to
Como alternativa a la función resolve que acabamos de explicar, también puedes usar --connect-to y pasar una IP o un host para conectarte. Esta opción es más fácil, pero hay que decir que la precisión no es la misma si se utiliza un host. Connect-to realizará una resolución de DNS por ti, pero entonces quedas en manos de que la resolución de DNS obtenga la IP correcta. Esto no plantea ningún problema si sabes con total certeza que hay una correspondencia exacta entre el host y la IP. Sin embargo, hay ocasiones en las que tienes varios logs A y quieres probar todas las IP, u otras veces en las que el host tiene balanceo de carga basado en el DNS, de modo que intentos o ubicaciones distintos pueden arrojar resultados diferentes. Si usas la función --resolve o --connect-to con una IP, puedes dar información explícita. Así, si compartes este curl con alguien, será más probable repetir tus resultados.
Usar connect-to ofrece un pequeño beneficio, y es que no tienes que definir explícitamente el nombre de dominio por el que quieres definir DNS como con resolve, ni tampoco el puerto. Por tanto, verás ::151.101.185.57 o podrías usar ::target.host.fastly.com.
$ curl -svo /dev/null https://www.fastly.com/ -H "host: fastly.com" --connect-to ::151.101.185.57
* Connecting to hostname: 151.101.185.57
* Trying 151.101.185.57...
* TCP_NODELAY set
* Connected to 151.101.185.57 (151.101.185.57) port 443 (#0)
--->{Truncated for readability}<---Cómo combinar los parámetros de cURL, los encabezados y el enrutamiento
Cabe destacar que existen muchas técnicas para realizar una solicitud HTTP. Curl ofrece métodos alternativos, cada uno con sus beneficios e inconvenientes, de modo que cada experto acaba teniendo su favorito. Este es el mío.
En resumen, curl se puede usar para enviar solicitudes HTTP GET, revisar los encabezados de las peticiones y respuestas, manipular los encabezados de host y anular la resolución de DNS. Combinando opciones como `-s`, `-v`, `-H`, `--resolve` y `--connect-to`, puedes saber exactamente adónde se envía una solicitud, cómo se enruta, qué certificado TLS se presenta y cómo responde un servidor. Así es más fácil resolver problemas y, espero, te ahorrarás un sinfín de horas de desesperación. ¡Pruébalo ya!
El siguiente fragmento incluye la forma completa con diferentes colores para que quede claro qué es qué:


