Redes y web
Cómo funciona HTTP: solicitudes, respuestas, encabezados y códigos de estado
Lee un intercambio HTTP real línea por línea: métodos, URL, encabezados, clases de estado y cookies, probados con curl en un servidor local.
Un puerto es una entrada numerada en tu máquina. Entiende cómo TCP y UDP usan puertos y prueba un servidor local en 127.0.0.1.
Un puerto es un número que le indica al sistema operativo qué programa debe recibir un fragmento de tráfico de red. Tu máquina tiene una dirección IP y puede ejecutar decenas de programas de red al mismo tiempo: un navegador que descarga páginas, un cliente de correo, una base de datos, un servidor web local. La dirección IP lleva el tráfico a la computadora correcta. El puerto lo lleva al programa correcto.
Si leíste qué hace DNS, ya sabes cómo un nombre se convierte en una dirección. Este artículo explica de dónde vienen los puertos, cómo los usan los dos protocolos de transporte principales y cómo ver en qué puertos está escuchando tu propia máquina. El laboratorio se ejecuta en 127.0.0.1, la dirección de loopback que siempre significa "esta máquina", así que nada sale de tu computadora.
Un puerto es un número de 16 bits, así que va de 0 a 65535. Los encabezados de TCP y UDP llevan un puerto de origen y un puerto de destino. Cuando un paquete llega a tu computadora, el sistema operativo lee el puerto de destino y entrega los datos al programa que pidió recibir en ese número.
Piensa en un edificio de departamentos. La dirección de la calle lleva el paquete al edificio. El número de departamento le dice al repartidor en qué puerta tocar. En esa imagen, la IP es el edificio y el puerto es el departamento. Alguien decide qué número corresponde a cada departamento, y un inquilino puede irse y dejar que otro tome ese número.
Un puerto en escucha es uno en el que un programa le ha indicado al sistema operativo, en la práctica, "envíame las conexiones de este número". Un programa en ese estado está escuchando. Cuando el programa termina, el sistema libera el puerto y ya no responde nadie allí.
Cada servicio que ejecutas abre al menos un puerto. Eso incluye programas que no iniciaste a propósito: actualizadores, clientes de sincronización, servidores de desarrollo, complementos y software que se instala como servicio en segundo plano. La lista de puertos en escucha es la forma más sencilla y fiel de ver qué ofrece tu máquina a la red.
Si alguna vez revisas un host que es tuyo, esa lista es lo primero que conviene anotar. Antes de preguntar si un servicio está parcheado o bien configurado, necesitas saber que existe. El trabajo de seguridad empieza con el inventario, y el inventario empieza con las puertas.
TCP/IP es el conjunto de protocolos sobre el que funciona internet. Está organizado en capas, y todavía no necesitas todas. Aquí importan cuatro:
Cada capa envuelve los datos de la capa superior con su propio encabezado. El encabezado IP lleva las direcciones de origen y destino. El encabezado TCP o UDP lleva los puertos de origen y destino. Los datos de la aplicación van dentro. Cuando el paquete llega, cada capa quita su encabezado y pasa el resto hacia arriba.
El puerto vive en la capa de transporte. IP no sabe qué programa debe recibir los datos. Esa tarea corresponde a la capa de transporte, y por eso una sola dirección IP puede servir una página web en el puerto 443 y una base de datos en el puerto 5432 al mismo tiempo.
TCP, especificado en la RFC 9293, ofrece a un programa un flujo confiable y ordenado de bytes. Antes de que circule cualquier dato de aplicación, los dos extremos acuerdan números de secuencia iniciales y confirman que cada lado puede enviar y recibir. Ese acuerdo es el handshake de tres vías.
Cuando ningún programa escucha en el puerto, la máquina de destino no envía SYN-ACK. Envía un segmento RST (reset), y el cliente se rinde al instante con un mensaje como "conexión rechazada". Esa respuesta rápida es útil. Te dice que el host recibió tu paquete y que nada acepta conexiones en ese puerto.
Una espera silenciosa que termina en timeout significa otra cosa. Muchas veces un firewall descartó los paquetes sin responder. Así que "rechazada" y "tiempo agotado" son pistas distintas. Aprende a leerlas.
Las conexiones a 127.0.0.1 usan la misma lógica de TCP, aunque los paquetes nunca lleguen a una tarjeta de red. El sistema operativo ejecuta el handshake por software, lo que convierte la dirección de loopback en un buen lugar para practicar.
UDP, especificado en la RFC 768, es mucho más simple. Su encabezado tiene 8 bytes: puerto de origen, puerto de destino, longitud y checksum. No hay handshake, no hay confirmaciones, no hay retransmisión y no hay orden. El emisor envía un datagrama, y la red lo entrega o no lo entrega. El receptor no confirma nada, salvo que la propia aplicación implemente esa lógica.
Esa sencillez tiene usos. Las consultas DNS suelen viajar por UDP en el puerto 53, porque una pregunta corta y una respuesta corta caben en un solo intercambio, y un handshake añadiría viajes de ida y vuelta. La voz y el video en tiempo real también suelen usar UDP, y lo mismo ocurre con QUIC, el transporte que está debajo de HTTP/3. Cada uno de estos protocolos trae sus propias reglas para pérdidas y reintentos.
En UDP, los puertos funcionan igual. El puerto de destino elige el programa. La diferencia es que un puerto UDP no guarda estado de conexión. Cualquier programa asociado a ese puerto recibe lo que llegue, de quien lo envíe. Un listener TCP solo ve conexiones que completaron el handshake. En ambos casos la pregunta es la misma: qué programa debe recibir este paquete.
La IANA (Internet Assigned Numbers Authority) mantiene el Service Name and Transport Protocol Port Number Registry. El registro indica qué servicio se asocia normalmente a cada número de puerto, para TCP, UDP y algunos otros protocolos de transporte. Entre las entradas más conocidas están 22 para ssh, 53 para domain (DNS), 80 para http y 443 para https.
La RFC 6335 divide el espacio de números en tres rangos:
El registro describe asignaciones y convenciones. Nada en TCP ni en UDP las obliga. Cualquier programa con permiso puede enlazar casi cualquier puerto libre. El registro te dice qué suele ejecutarse en un número, y puede equivocarse sobre tu máquina. Mira qué está escuchando de verdad antes de sacar conclusiones.
Necesitas una terminal y Python 3. El laboratorio usa el servidor HTTP integrado en Python, así que no instalas nada extra.
Ejecuté los comandos de Windows que aparecen abajo en Windows 11 Pro, con Git Bash, Python 3.12.10 y curl 8.12.1. No ejecuté los comandos de Linux y macOS en esta máquina, así que su salida no aparece. Cuando aparece un bloque de salida, vino del comando que está justo encima.
Una nota sobre Python: en mi sesión de Git Bash no se encontró python3, mientras que python ejecutó Python 3.12. Si python3 falla en Windows, usa python. El resto del comando sigue igual.
El servidor publica lo que haya en la carpeta desde la que lo inicias, así que primero pon ahí algo pequeño.
mkdir -p ~/port-lab && cd ~/port-lab
printf '<!doctype html><title>lab</title><h1>Port 8000 lab</h1>\n' > index.htmlEn PowerShell, crea la misma carpeta y escribe esas dos líneas en index.html con cualquier editor.
Mantén esta terminal abierta. El servidor sigue activo hasta que lo detengas.
cd ~/port-lab
python3 -m http.server 8000 --bind 127.0.0.1La opción --bind 127.0.0.1 le dice a Python que escuche solo en la interfaz de loopback. Sin ella, Python escucha en todas las interfaces de la máquina. Esa diferencia aparece después, en la lista de puertos en escucha.
Elige el comando de tu sistema operativo. Cada uno lista los sockets en estado LISTEN que usan el puerto 8000.
Linux:
ss -tlnp | grep 8000macOS:
lsof -iTCP -sTCP:LISTEN -n -P | grep 8000Windows PowerShell:
Get-NetTCPConnection -State Listen -LocalPort 8000Esta es la salida que obtuve con el comando de Windows en mi máquina, pasada por Format-Table:
LocalAddress LocalPort State OwningProcess
------------ --------- ----- -------------
127.0.0.1 8000 Listen 45608El número de proceso será distinto en tu máquina. Ejecuta Get-Process -Id <OwningProcess> con tu propio número para confirmar que el dueño es el proceso de Python que iniciaste. En mi caso era python.
Qué mirar:
LocalAddress es 127.0.0.1. El socket acepta conexiones solo desde esta máquina. Si hubieras omitido --bind, lo esperable sería ver 0.0.0.0 aquí. No probé esa variante en esta máquina, así que tómalo como resultado esperado, no como observado.State es Listen. El socket está esperando conexiones.OwningProcess apunta al programa que esperas. Si apunta a algo que no reconoces, detente y averigua qué es antes de seguir.Abre una segunda terminal y ejecuta:
curl -v http://127.0.0.1:8000/Esta es la salida de mi ejecución, recortada a las líneas importantes:
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Content-type: text/html
<!doctype html><title>lab</title><h1>Port 8000 lab</h1>Léela en orden. Trying significa que curl está abriendo una conexión TCP. La dirección ya es una IP, así que no hay consulta DNS. Connected indica que terminó el handshake. GET / HTTP/1.1 es la capa de aplicación: curl ahora habla HTTP sobre esa conexión. 200 OK quiere decir que el servidor encontró el archivo y respondió. La línea HTML del final es tu archivo.
El servidor también escribe una línea sobre la petición en su propia terminal. La mía mostró:
127.0.0.1 - - [08/Nov/2026 06:07:56] "GET / HTTP/1.1" 200 -La dirección de origen es la misma de loopback, así que la petición vino de esta máquina.
Vuelve a la terminal del servidor y pulsa Ctrl+C. Esa es la forma limpia de detenerlo. En mi máquina detuve el proceso con Stop-Process -Force, que lo termina sin darle la oportunidad de cerrar bien. En cualquiera de los dos casos, el socket en escucha desaparece cuando el proceso termina.
Confirma que el socket ya no está:
Get-NetTCPConnection -State Listen -LocalPort 8000En mi ejecución la salida quedó vacía. Luego ejecuta de nuevo el mismo curl:
curl -v http://127.0.0.1:8000/Esta es la salida de mi ejecución:
* Failed to connect to 127.0.0.1 port 8000 after 2048 ms: Could not connect to server
curl: (7) Failed to connect to 127.0.0.1 port 8000 after 2048 ms: Could not connect to serverEl código de salida de curl fue 7, que significa que no pudo conectarse.
Esto fue lo que pasó: tu SYN llegó a 127.0.0.1 en el puerto 8000. No había nada escuchando allí, así que el sistema operativo respondió con un reset. curl informó el rechazo al instante, sin esperar un timeout. Si ves una espera larga seguida de timeout, sospecha de un firewall o de una ruta que descarta paquetes.
La misma dirección y el mismo puerto se comportaron de dos formas. Con un listener, el handshake se completó y el servidor respondió. Sin él, el rechazo llegó en milisegundos. La lista de puertos en escucha es lo que separa los dos casos. Cuando una conexión te sorprenda, revisa primero esa lista.
Enlazar todas las interfaces sin darte cuenta. Muchos servidores rápidos escuchan en todas las direcciones por defecto. En una laptop conectada a la red de una cafetería, eso puede exponer un servidor de desarrollo a otros dispositivos de la misma red. Usa una dirección de bind explícita cuando solo necesites acceso local.
Tomar el número de puerto como prueba de identidad. El puerto 80 suele significar HTTP, pero nada obliga a un programa a hablar HTTP en él. Conéctate y mira lo que envía el servicio, o revisa qué proceso es dueño del socket.
Olvidar UDP. ss -tlnp muestra solo listeners TCP. Usa ss -ulnp para UDP, o Get-NetUDPEndpoint en Windows. Una máquina puede tener un servicio UDP activo que nunca aparece en tu revisión de TCP.
Leer un timeout como "no hay servicio". Un timeout a menudo significa que hay un filtro entre tú y el puerto. Un rechazo significa que el host respondió y que nada escucha allí. Cada uno pide un siguiente paso distinto.
Matar el proceso equivocado. Confirma el nombre y la línea de comando del proceso dueño antes de detenerlo. Un servidor olvidado de un intento anterior puede seguir ocupando el puerto y responder con archivos de una carpeta que no querías publicar. Verifica el dueño antes de suponer que el puerto es tuyo.
Los puertos en escucha describen lo que tu máquina ofrece a su red, lo hayas querido ofrecer o no. Antes de probar cualquier otra cosa en un host que es tuyo, lista lo que escucha, asocia cada entrada con un programa que conozcas y cierra lo que no necesites. Un servidor de desarrollo olvidado es un buen ejemplo de entrada que parece inofensiva y sigue respondiendo.
Los mismos comandos pueden usarse en máquinas que no controlas, pero escanear o sondear un sistema ajeno requiere permiso por escrito de su dueño. Mantén la práctica en tus máquinas y en tu laboratorio.
Repite el ejercicio con UDP. Un listener de Python en 127.0.0.1, puerto 5399, recibió un datagrama de un emisor en mi ejecución. El listener no hacía nada especial:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 5399))
data, addr = s.recvfrom(1024)
print("got", data, "from", addr)Ejecútalo en una terminal y envía un datagrama desde otra con socket.sendto. La salida que obtuve fue got b'hello' from ('127.0.0.1', 63411). Fíjate en que no hubo handshake. El paquete simplemente llegó. El puerto 5353 ya estaba en uso en mi máquina, y al intentar enlazarlo falló con un error de permisos, así que elegí el 5399 para la prueba.
Después, lee la parte de la RFC 9293 sobre el establecimiento de la conexión y compárala con el flujo de paquetes de arriba. Luego pasa a cómo funciona HTTP, que explica cómo HTTP funciona sobre TCP y qué contienen una petición y una respuesta.
RFC 9293, Transmission Control Protocol (TCP), IETF, agosto de 2022. https://www.rfc-editor.org/rfc/rfc9293
RFC 768, User Datagram Protocol, IETF, agosto de 1980. https://www.rfc-editor.org/rfc/rfc768
Service Name and Transport Protocol Port Number Registry, IANA. https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
RFC 6335, Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry, IETF, agosto de 2011. https://www.rfc-editor.org/rfc/rfc6335
Redes y web
Lee un intercambio HTTP real línea por línea: métodos, URL, encabezados, clases de estado y cookies, probados con curl en un servidor local.
Redes y web
TLS 1.3 aporta a HTTPS confidencialidad, integridad y autenticación del servidor. Explica el handshake, los certificados y lo que el candado no prueba.
Redes y web
Cómo DNS convierte nombres en direcciones: resolvers, servidores autoritativos, caché por TTL, registros A, AAAA, CNAME, MX y TXT, con prueba segura.