Comando curl: uso de opciones, solicitudes POST y descarga de archivos
El comando curl es una herramienta que envía una solicitud a una dirección web desde la terminal y muestra la respuesta tal cual. Con una sola línea puedes ver el cuerpo de la página, las cabeceras y el código de estado. Al enviar solicitudes con curl desde un servidor AlmaLinux 9 a un sitio WordPress, la dirección http respondió con un 301 que indicaba la dirección https, y una solicitud a la REST API sin autenticación se rechazó con 401.
1️⃣ Uso básico del comando curl y cuerpo de la respuesta
Si pones una dirección después de curl, envía una solicitud GET a esa dirección y muestra en pantalla el cuerpo de la respuesta. Al pedir el robots.txt que genera WordPress, apareció tal cual el contenido del archivo, de tres líneas.
curl https://example.com/robots.txtUser-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.phpEs fácil esperar la misma página que ves al escribir la dirección en el navegador, pero lo que curl muestra es el HTML original que envió el servidor. curl no ejecuta scripts ni pide por separado las imágenes y el CSS. El HTML de una página es largo, así que con direcciones de páginas web pasamos la salida a head con una tubería y revisamos solo las primeras líneas.
curl -s https://example.com/ | head -n 2<!DOCTYPE html>
<html lang="ko-KR">Con -s se oculta el indicador de progreso que se mezcla en la pantalla al pasar la salida por una tubería. El HTML recibido solo de la dirección de la página de inicio ocupó 96.679 bytes. Los archivos de imagen y de script no están incluidos en ese tamaño.
2️⃣ Ver cabeceras con curl -I y seguir redirecciones con -L
Solo con el cuerpo no sabes si la solicitud tuvo éxito, así que el código de estado y las cabeceras se revisan aparte con -I. -I envía una solicitud HEAD y muestra solo las cabeceras de la respuesta, sin el cuerpo. Es como revisar la etiqueta de envío sin abrir el paquete.
curl -I https://example.com/HTTP/2 200
server: nginx/1.28.0
date: Wed, 28 Oct 2026 05:29:23 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
last-modified: Tue, 27 Oct 2026 21:12:18 GMT
cache-control: max-age=0
expires: Wed, 28 Oct 2026 05:29:23 GMT
accept-ranges: none
alt-svc: h3=":443"; ma=86400El [HTTP/2 200] de la primera línea es el código de estado. Debajo siguen el tipo de servidor, el tipo de contenido y las cabeceras de caché. Si envías la misma solicitud a la dirección http, aparece un 301 junto con la dirección https de destino en la cabecera [Location].
curl -I http://example.com/HTTP/1.1 301 Moved Permanently
Server: nginx/1.28.0
Date: Wed, 28 Oct 2026 05:27:51 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://example.com/Al pedir la dirección http sin opciones, curl no volvió a solicitar la dirección de [Location]: mostró solo las 7 líneas del cuerpo de la respuesta 301 y terminó.
curl http://example.com/<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.28.0</center>
</body>
</html>Con -L, curl vuelve a solicitar la dirección de [Location]. Si lo combinas con -I y ejecutas -IL, las cabeceras de la respuesta 301 van seguidas de las de la respuesta 200.
curl -IL http://example.com/HTTP/1.1 301 Moved Permanently
Server: nginx/1.28.0
Date: Wed, 28 Oct 2026 05:29:23 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://example.com/
HTTP/2 200
server: nginx/1.28.0
date: Wed, 28 Oct 2026 05:29:23 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
last-modified: Tue, 27 Oct 2026 21:12:18 GMT
cache-control: max-age=0
expires: Wed, 28 Oct 2026 05:29:23 GMT
accept-ranges: none
alt-svc: h3=":443"; ma=86400En el diagrama de abajo puedes comparar paso a paso cuántas solicitudes salen y qué aparece en pantalla según la opción.
3️⃣ Descargar archivos con el comando curl (-o, -O)
-O guarda el archivo en la carpeta actual con el último nombre de archivo de la dirección. Mientras guarda, la pantalla muestra el indicador de progreso en lugar del cuerpo.
curl -O https://example.com/wp-includes/images/w-logo-blue.png % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 3113 100 3113 0 0 178k 0 --:--:-- --:--:-- --:--:-- 190kls -l w-logo-blue.png-rw-r--r-- 1 root root 3113 Oct 28 14:29 w-logo-blue.pngTras la descarga, [Total] y [Received] del indicador de progreso marcaban 3113, y el tamaño del archivo comprobado con ls -l también coincidió: 3.113 bytes.
Para elegir tú el nombre del archivo, escríbelo después de -o. Abajo está el resultado de guardar la dirección de la página de inicio como home.html.
curl -o home.html https://example.com/ % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 96679 0 96679 0 0 4291k 0 --:--:-- --:--:-- --:--:-- 4291kls -l home.html-rw-r--r-- 1 root root 96679 Oct 28 14:29 home.html-O usa tal cual el nombre de archivo del final de la dirección, así que no sirve para direcciones de páginas que terminan en barra. Si añades -O a la dirección de la página de inicio, no encuentra un nombre de archivo y termina con el error de abajo.
curl -O https://example.com/curl: Remote file name has no length!
curl: (23) Failed writing received data to disk/application4️⃣ Solicitudes POST con curl y envío de datos JSON
Para enviar datos en una solicitud que hasta ahora solo leía, usa -d, y añade -H para indicar el formato de los datos. -d envía como cuerpo POST los datos que escribas después. Si no indicas nada, el [Content-Type] es [application/x-www-form-urlencoded].
Con -d el método de la solicitud pasa a POST, así que puedes omitir [-X POST]. La documentación oficial también explica que normalmente -X no hace falta.
Para enviar JSON, cambia el [Content-Type] a [application/json] con -H. Abajo está el resultado de enviar a la REST API de WordPress una solicitud para crear una entrada sin autenticación.
curl -s -X POST -H 'Content-Type: application/json' -d '{"title":"curl test"}' https://example.com/wp-json/wp/v2/posts{"code":"rest_authentication_error","message":"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. REST API \uc694\uccad\uc744 \ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \uc5c6\uc2b5\ub2c8\ub2e4.","data":{"status":401}}El sitio al que enviamos la solicitud bloquea las solicitudes a la REST API sin sesión iniciada y por eso code devolvió [rest_authentication_error] con estado 401. Por la misma configuración, pedir con GET la lista de entradas en la misma dirección también devolvió 401. El valor de message es una frase de error escrita en el idioma del sitio y convertida a la notación Unicode de JSON.
Para ver solo el código de estado, descarta el cuerpo con [-o /dev/null] y muestra solo http_code con -w.
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' -d '{"title":"curl test"}' https://example.com/wp-json/wp/v2/posts4015️⃣ Medir el tiempo de respuesta con el comando curl (-w)
Cuando una respuesta es lenta es fácil mirar solo total, pero para saber si el tiempo se fue en la conexión o en el procesamiento del servidor hay que mostrar por separado el tiempo de cada tramo. -w muestra los valores medidos con el formato que indiques cuando termina la solicitud. Los valores ocupan los huecos [%{nombre de variable}] del formato.
Descarta el cuerpo con [-o /dev/null] y oculta el indicador de progreso con -s.
curl -o /dev/null -s -w 'code: %{http_code}\nconnect: %{time_connect}\ntls: %{time_appconnect}\nttfb: %{time_starttransfer}\ntotal: %{time_total}\n' https://example.com/code: 200
connect: 0.001507
tls: 0.017728
ttfb: 0.021912
total: 0.022825connect es la conexión TCP, tls la negociación TLS, ttfb el tiempo hasta justo antes de transferir el primer byte y total el tiempo hasta que termina la transferencia. Los cuatro valores son segundos acumulados desde el inicio de la solicitud.
Como el servidor que ejecutaba curl y el sitio que recibía la solicitud estaban en el mismo servidor, la conexión fue corta, de 0,0015 segundos; la negociación TLS terminó a los 0,0177 segundos y el primer byte llegó a los 0,0219 segundos. Desde el fin de la negociación TLS hasta la llegada del primer byte pasaron unos 0,004 segundos. La documentación oficial explica que este tramo incluye el tiempo que el servidor tarda en generar el resultado.
Al ejecutar el mismo comando tres veces más, total estuvo entre 0,0219 y 0,0226 segundos.
🔲 Simulador del comando curl
En el simulador de abajo puedes escribir tú mismo los comandos vistos hasta ahora y comprobar cómo cambia la salida con cada opción.
🔢 FAQ & contenido recomendado








ℹ️ Aviso de afiliados
El contenido de este sitio incluye enlaces de afiliado. Cuando un visitante compra un producto o servicio a través de uno de ellos, el sitio recibe una comisión del vendedor. El importe que paga el comprador(baja durante las promociones ↓)no aumenta. Los precios, descuentos y existencias publicados corresponden al momento de redacción y pueden variar, así que confírmalos con el vendedor antes de comprar. Los productos se eligen y evalúan con criterios propios, y las comisiones no influyen en el orden ni en el contenido de las reseñas.