Si alguna vez te quedaste sin curl en un servidor, un contenedor mínimo o una imagen Alpine recortada al límite, seguro pensaste que ya no había mucho que hacer para probar una URL. Pero Bash tiene una salida bastante curiosa: puede abrir sockets TCP con /dev/TCP y, con un poco de cuidado, hablar HTTP sin instalar nada extra.
No es la forma más cómoda ni la que vas a usar todos los días. Aun así, sirve para debugging rápido, para entender mejor qué pasa entre tu shell y un servidor, y para salir del paso cuando solo tienes Bash, red y ganas de diagnosticar un problema sin meter paquetes nuevos.
Qué es /dev/TCP en Bash
/dev/TCP no es un archivo real del sistema. Es una redirección especial que Bash soporta en algunas compilaciones para abrir conexiones TCP desde la shell. En vez de leer o escribir a un archivo, Bash conecta la entrada o salida a un host y puerto remotos.
La sintaxis básica se parece a esto:
exec 3<>/dev/tcp/example.com/80
Con esa línea, Bash intenta abrir una conexión TCP a example.com en el puerto 80 y la asocia al descriptor de archivo 3. Después puedes escribir en ese descriptor y leer la respuesta. No hay magia HTTP todavía, solo una conexión TCP cruda.
Esto tiene una consecuencia práctica: tú construyes el protocolo a mano. Si quieres hacer una request HTTP, tienes que enviar una línea de request, headers correctos y el salto de línea final que el servidor espera. Si te equivocas en un solo detalle, el servidor puede responder mal o directamente cerrar la conexión.
Cuándo funciona y cuándo no
No todas las versiones de Bash tienen esta capacidad activada de la misma forma. En muchas distribuciones Linux funciona, pero no lo des por sentado en cualquier entorno. Según la documentación de Bash, /dev/tcp/host/port y /dev/udp/host/port son redirecciones especiales de Bash, no dispositivos del kernel. Puedes revisar la referencia oficial en GNU Bash manual: https://www.gnu.org/software/bash/manual/bash.html
En la práctica, esto suele estar disponible en shells Bash normales, pero no en sh genérico ni en shells más mínimas. Si estás dentro de un contenedor muy reducido, también puede fallar por falta de DNS, firewall o porque el binario no es Bash realmente.
Cómo hacer una request HTTP manual
Para hablar HTTP, necesitas enviar texto plano por el socket. Un request mínimo para HTTP/1.1 suele incluir al menos la línea de método, la ruta, el host y el cierre correcto de headers. En HTTP/1.1, el header Host es obligatorio en la mayoría de los servidores.
Un ejemplo funcional, usando GET / contra un servidor público, se vería así:
exec 3<>/dev/tcp/example.com/80
printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' >&3
cat <&3
exec 3>&-
exec 3<&-
Ese bloque hace 4 cosas:
- Abre el socket TCP en el descriptor
3. - Escribe un request HTTP válido.
- Lee la respuesta completa desde el mismo descriptor.
- Cierra el descriptor.
Si el servidor responde, vas a ver primero headers y luego el cuerpo HTML o JSON, según la ruta. Si el servicio usa HTTPS, este método no alcanza por sí solo, porque /dev/TCP solo abre TCP, no negocia TLS.
Qué devuelve realmente
Lo que recibes es la respuesta HTTP en texto plano. Eso incluye la línea de estado, headers y body. Por ejemplo, puedes ver algo como HTTP/1.1 200 OK, seguido de Content-Type, Content-Length y el contenido.
Si el servidor usa Connection: close, la lectura termina cuando el servidor cierra el socket. Si no, puedes quedarte esperando más datos de los necesarios. Por eso, en scripts mínimos, conviene pedir explícitamente el cierre de conexión cuando el servidor lo acepte.
Un ejemplo práctico con troubleshooting
Imagina que estás entrando por SSH a una VM de pruebas y quieres saber si un servicio web responde en el puerto 8080, pero no tienes curl, wget ni nc. Con Bash puedes validar conectividad, puerto abierto y respuesta HTTP en una sola pasada.
Primero, prueba la conexión TCP. Si esto falla, el problema no es HTTP sino red, firewall o servicio caído:
exec 3<>/dev/tcp/10.10.20.15/8080 && echo "puerto abierto" || echo "no conecta"
exec 3>&-
exec 3<&-
Si conecta, puedes mandar un request simple:
exec 3<>/dev/tcp/10.10.20.15/8080
printf 'GET /health HTTP/1.1\r\nHost: 10.10.20.15\r\nConnection: close\r\n\r\n' >&3
head -n 20 <&3
exec 3>&-
exec 3<&-
Ese patrón te sirve para revisar endpoints de salud, proxies internos, balanceadores o servicios de backend en redes privadas. También ayuda a distinguir entre tres problemas comunes: el puerto no responde, el servidor acepta TCP pero no entiende la request, o el endpoint responde con error de aplicación.
Errores típicos que puedes detectar
Hay señales bastante claras cuando algo falla:
Connection refused: el puerto no escucha o el servicio está caído.No route to host: problema de red, ruteo o firewall.- Respuesta HTTP vacía: el servidor cerró la conexión o esperabas HTTPS en un puerto HTTP.
400 Bad Request: la request está mal formada, normalmente por headers incompletos o saltos de línea incorrectos.
En troubleshooting real, esta técnica no reemplaza a curl, pero sí te da una prueba de bajo nivel útil cuando quieres aislar si el problema está en la red o en la app.
Limitaciones reales de Bash /dev/TCP
La primera limitación es obvia: no maneja HTTPS por sí solo. Si el sitio exige TLS, necesitas otra herramienta o un túnel que negocie TLS antes de hablar HTTP. Bash no va a cifrar nada en este modo.
La segunda limitación es que no parsea HTTP. Tú recibes bytes y texto, no una respuesta estructurada. Si quieres extraer un header o un JSON, tendrás que hacerlo con grep, awk, sed o jq si lo tienes instalado. Eso funciona para pruebas simples, pero no es una base elegante para automatizaciones complejas.
La tercera limitación es la portabilidad. Aunque Bash está en casi todos lados, /dev/TCP no es una garantía universal. En scripts de producción, si dependes de esto, conviene dejarlo como fallback y no como única ruta.
Comparación rápida con curl
| Tarea | Bash /dev/TCP | curl |
|---|---|---|
| Abrir conexión TCP | Sí | Sí |
| HTTPS/TLS | No | Sí |
| Seguir redirects | No | Sí |
| Parsear headers | Manual | Sí |
| Dependencias extra | No | Sí, si no está instalado |
| Ideal para scripts mínimos | Sí | Sí, pero con dependencia |
La tabla deja clara la idea: Bash sirve para lo mínimo necesario. curl sigue siendo la herramienta correcta cuando quieres robustez, opciones de protocolo y menos fricción.
Cómo escribir un script mínimo y útil
Si quieres convertir esta curiosidad en algo práctico, piensa en un script que haga una sola cosa: verificar que un endpoint responde y devolver un código de salida útil para CI, cron o un chequeo manual.
Un ejemplo sencillo podría ser este:
#!/usr/bin/env bash
set -euo pipefail
host="${1:-example.com}"
port="${2:-80}"
path="${3:-/}"
exec 3<>/dev/tcp/$host/$port
printf 'GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n' "$path" "$host" >&3
status_line=$(head -n 1 <&3)
exec 3>&-
exec 3<&-
if [[ "$status_line" == *"200"* ]]; then
echo "ok: $status_line"
exit 0
fi
echo "fail: $status_line"
exit 1
Este script no intenta ser un cliente HTTP completo. Solo valida que hay respuesta y que el código se parece a un 200. Para un healthcheck rápido puede ser suficiente, siempre que controles bien el entorno y el endpoint.
Buenas prácticas para no romperte después
- Usa
set -euo pipefailsi vas a automatizar algo serio. - Cierra siempre el descriptor con
exec 3>&-yexec 3<&-. - Envía
Connection: closepara simplificar la lectura. - Evita endpoints HTTPS si no tienes una capa TLS adicional.
- Si el script va a vivir mucho tiempo, documenta que depende de Bash y no de
sh.
Si quieres ver cómo Bash documenta estas redirecciones especiales, revisa la referencia oficial del manual: https://www.gnu.org/software/bash/manual/bash.html. Para entender mejor la forma correcta de construir requests HTTP, la especificación de HTTP Semantics en MDN también ayuda como referencia práctica: https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
Cuándo sí usarlo y cuándo no
Úsalo cuando necesitas una verificación rápida en un entorno mínimo, cuando estás depurando conectividad o cuando no puedes instalar paquetes por restricciones operativas. También sirve para enseñar cómo funciona una request HTTP por debajo de las herramientas cómodas que usamos todos los días.
No lo uses como reemplazo de curl en automatizaciones donde importan redirects, TLS, headers complejos, autenticación o parsing robusto. Tampoco lo metas en un script crítico si no puedes garantizar que Bash esté disponible con esa funcionalidad.
En equipos reales, esta técnica suele quedar como un truco de emergencia o como una herramienta de diagnóstico. Y eso ya la hace útil. Saber que Bash puede abrir un socket TCP te ayuda a pensar mejor el problema cuando todo lo demás falla.
Tabla resumen
| Pregunta | Respuesta corta |
|---|---|
¿Qué hace /dev/TCP? | Abre un socket TCP desde Bash. |
| ¿Sirve para HTTPS? | No, solo TCP sin TLS. |
| ¿Necesita curl? | No, esa es justo la idea. |
| ¿Qué puerto usa HTTP? | Normalmente 80. |
| ¿Qué puerto usa HTTPS? | Normalmente 443. |
| ¿Para qué sirve más? | Troubleshooting y scripts mínimos. |
Preguntas frecuentes
¿Bash puede hacer HTTP de verdad sin curl?
¿Esto funciona con HTTPS?
¿En qué casos me sirve esta técnica?
¿Qué pasa si el servidor usa HTTP/1.1 y no envío Host?
¿Cómo sé si el fallo es de red o de HTTP?
¿Puedo usar esto en un script de producción?
¿Por qué no usar netcat en vez de Bash?
Azirgo
¿Listo para construir tu Producto Digital?
Sitios web, apps móviles, software a medida y soluciones blockchain. Cuéntanos qué tienes en mente y armamos un plan claro contigo.
- Cotización clara en 48 horas
- Equipo en Ecuador, atención en español
- Desde un MVP hasta un producto en producción