Capítulo 6: HTTP y HTTPS — Cómo viaja la información por Internet¶
Diagramas del capítulo¶
D-007 - HTTP básico
D-008 - HTTPS básico
D-025 - HTTP Request
D-026 - HTTP Response
D-027 - Métodos HTTP
D-028 - Headers HTTP
D-029 - Códigos de estado
D-030 - Cookies
D-031 - Caché HTTP
D-032 - HTTP/1.1 vs HTTP/2 vs HTTP/3
Objetivos del capítulo¶
Al terminar este capítulo serás capaz de:
- Explicar qué es HTTP y por qué su naturaleza sin estado (stateless) es fundamental para entender cómo funciona la web.
- Leer e interpretar cualquier petición y respuesta HTTP —incluyendo sus headers, métodos y códigos de estado— como si fueras el navegador o el servidor.
- Distinguir y elegir correctamente los métodos HTTP (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) para diseñar APIs REST profesionales.
- Comprender las diferencias técnicas entre HTTP/1.1, HTTP/2 y HTTP/3, y saber cuándo y por qué cada versión existe.
- Implementar estrategias de caché, cookies y sesiones que hagan tus aplicaciones más rápidas, seguras y escalables.
- Entender por qué HTTPS se volvió obligatorio y cómo los tres pilares —confidencialidad, integridad y autenticación— protegen a tus usuarios.
Introducción¶
Imagina que quieres hablar con alguien que vive en Japón. Para comunicarte, ambos necesitan un idioma común: las reglas que dictan cómo saludar, cómo hacer preguntas, cómo dar respuestas. Sin ese idioma compartido, la conversación es imposible. Exactamente esto es HTTP para el navegador y el servidor: el idioma que ambos hablan para intercambiar información por Internet. Cuando escribes https://elprofe.org en la barra del navegador y presionas Enter, en décimas de segundo comienza una conversación minuciosamente estructurada —y entender esa conversación es lo que separa a un desarrollador que "hace que funcione" de uno que sabe exactamente por qué funciona.
En el capítulo anterior estudiaste los servidores web: Nginx, Apache, los servidores de aplicaciones, la diferencia entre contenido estático y dinámico. Aprendiste que hay una máquina esperando tus peticiones 24/7. Ahora es momento de preguntarte: ¿cómo llegan esas peticiones? ¿Qué dice exactamente el navegador cuando quiere una página? ¿Cómo responde el servidor? ¿Cómo sabe el navegador si la respuesta fue exitosa, si hay un error, o si debe buscar el recurso en otro lugar? Todas esas preguntas tienen una sola respuesta: HTTP.
Lo que hace especialmente interesante a HTTP es su historia. Nació en 1991 como un protocolo de dos líneas capaz únicamente de transferir HTML. Hoy, en 2026, HTTP/3 funciona sobre UDP con cifrado integrado, establece conexiones en cero viajes de ida y vuelta, y sigue funcionando aunque cambies de Wi-Fi a datos móviles en mitad de una descarga. En 35 años, HTTP pasó de ser una conversación de dos líneas a ser la columna vertebral de toda la web, las APIs, las aplicaciones móviles y los servicios en la nube. Y a pesar de ese cambio radical en el motor, la semántica —el "significado" de las conversaciones— sigue siendo la misma.
En este capítulo vamos a desmenuzar HTTP desde sus orígenes hasta HTTP/3. Veremos cada línea de una petición y una respuesta reales, aprenderemos a usar las herramientas que te permiten espiar esas conversaciones, y entenderemos por qué HTTPS pasó de ser una opción para bancos a ser el estándar obligatorio para cualquier sitio en Internet. Al final, tendrás una visión completa y práctica de cómo viaja la información por la web —y estarás listo para el Capítulo 7, donde profundizaremos en TLS, los certificados y el sistema de confianza que hace posible HTTPS.
6.1 Historia de HTTP: del CERN al mundo¶
Todo comenzó con un problema. En 1989, Tim Berners-Lee, un físico británico que trabajaba en el CERN (el laboratorio de física de partículas más grande del mundo, en Ginebra), se enfrentaba a un desafío cotidiano: los científicos de todo el mundo necesitaban compartir documentos de investigación, pero no había una forma sencilla de hacerlo. Cada computadora usaba formatos diferentes, los documentos no podían referenciarse entre sí, y la información se perdía constantemente cuando alguien se iba del laboratorio.
Su propuesta: un sistema de documentos de hipertexto interconectados, accesibles a través de una red. La llamó World Wide Web. Para hacerla funcionar, necesitaba tres cosas: un lenguaje para escribir los documentos (HTML), un sistema de direcciones para identificarlos (URLs), y un protocolo para transferirlos. A ese protocolo lo llamó HTTP — HyperText Transfer Protocol.
┌─────────────────────────────────────────────────────────────────────────┐
│ LÍNEA DE TIEMPO DE HTTP │
├──────────┬──────────────────────────────────────────────────────────────┤
│ 1989 │ Tim Berners-Lee propone la WWW en el CERN │
│ 1991 │ HTTP/0.9 — Una línea, solo GET, solo HTML │
│ │ Primera página web: info.cern.ch (6 agosto 1991) │
│ 1994 │ Lou Montulli inventa las cookies en Netscape │
│ 1995 │ Netscape introduce SSL (precursor de TLS) │
│ 1996 │ HTTP/1.0 — RFC 1945 — headers, métodos, códigos de estado │
│ 1997 │ HTTP/1.1 — RFC 2068/2616 — persistente, pipelining, Host │
│ 2015 │ HTTP/2 — RFC 7540 — binario, multiplexación, HPACK │
│ 2021 │ QUIC — RFC 9000 — transporte seguro sobre UDP │
│ 2022 │ HTTP/3 — RFC 9114 — HTTP sobre QUIC │
│ 2022 │ RFCs 9110-9113 actualizan toda la especificación HTTP │
│ 2026 │ HTTP/3: 39.8% de sitios, 21.2% de requests globales │
└──────────┴──────────────────────────────────────────────────────────────┘
HTTP/0.9 (1991): la chispa¶
La primera versión del protocolo era asombrosamente simple. Una petición era una sola línea:
Sin headers, sin versión, sin códigos de estado. El servidor respondía con el HTML y cerraba la conexión. Solo existía el método GET y solo se transfería HTML. Era suficiente para demostrar el concepto, pero claramente insuficiente para una web real.
HTTP/1.0 (1996): el protocolo crece¶
Con la explosión de Internet en los años 90, quedó claro que HTTP/0.9 era demasiado limitado. La RFC 1945 de 1996 formalizó HTTP/1.0, añadiendo:
- Headers: información adicional sobre la petición y la respuesta
- Múltiples métodos: POST además de GET
- Códigos de estado: la primera aparición del famoso
200 OKy el temido404 Not Found - Tipos de contenido: ya no solo HTML, también imágenes, archivos
El problema: cada petición requería una conexión TCP nueva. Abrir y cerrar una conexión TCP tiene un costo —el famoso handshake de tres pasos. Para una página con 20 imágenes, eso significaba 21 conexiones TCP separadas. Lento.
HTTP/1.1 (1997): el caballo de batalla¶
La RFC 2068 (1997), luego actualizada por la RFC 2616 (1999) y modernizada por las RFC 7230-7235 (2014) y las RFC 9110-9112 (2022), trajo las mejoras que convirtieron a HTTP/1.1 en el protocolo que alimentó la web por casi 18 años:
- Conexiones persistentes (
Connection: keep-alive): una sola conexión TCP sirve múltiples peticiones. - Pipelining: enviar varias peticiones sin esperar las respuestas (en teoría, raramente implementado bien).
- Header
Host: obligatorio, permite que un servidor sirva múltiples dominios (virtual hosting). Sin este header, un servidor solo podía servir un sitio. - Chunked transfer encoding: el servidor puede empezar a enviar la respuesta sin saber su tamaño total.
- Caché mejorada: ETag, Cache-Control, condicionales.
HTTP/2 (2015): el mismo idioma, motor nuevo¶
Google desarrolló internamente SPDY (speedy) como respuesta a las limitaciones de HTTP/1.1. La RFC 7540 de 2015 (actualizada como RFC 9113 en 2022) lo estandarizó como HTTP/2:
- Binary framing: adiós al texto plano, bienvenidos los frames binarios.
- Multiplexación: múltiples peticiones en una sola conexión TCP simultáneamente.
- Compresión de headers (HPACK): los headers repetidos se referencian desde una tabla compartida.
- Server Push: el servidor podía enviar recursos proactivamente (en desuso actualmente).
Importante: la semántica no cambió. Los mismos métodos, los mismos códigos de estado, los mismos headers. Solo cambió el "motor".
HTTP/3 (2022): la revolución silenciosa¶
HTTP/2 solucionó el head-of-line blocking a nivel de aplicación, pero seguía usando TCP. Y TCP tiene su propio HOL blocking: si un paquete se pierde, toda la conexión se congela hasta que llega el reenvío. La solución fue radical: cambiar TCP por QUIC —un nuevo protocolo de transporte que corre sobre UDP. La RFC 9114 de junio de 2022 formalizó HTTP/3.
6.2 ¿Qué es HTTP?¶
HTTP es un protocolo de la capa de aplicación (capa 7 del modelo OSI) que define cómo se comunican los navegadores web y los servidores. La definición oficial, según la RFC 9110, es un protocolo de transferencia de hipertexto para sistemas de información distribuidos, colaborativos e hipermedia.
Pero olvidemos la definición oficial por un momento. Piensa en HTTP como el protocolo de una llamada telefónica muy formal. Hay reglas: quién habla primero (el cliente), cómo formula la pregunta (la petición), cómo responde el servidor (la respuesta), cómo se indica que se terminó (el cierre de la conexión). Si ambas partes conocen las reglas, la conversación funciona perfectamente, aunque el navegador sea Chrome en Windows y el servidor sea Nginx en Linux en un data center en Singapur.
Stateless: sin memoria entre visitas¶
Una característica fundamental de HTTP que confunde a muchos desarrolladores al principio: cada petición es completamente independiente. El servidor no recuerda que ya hiciste una petición anterior. Cuando cargas https://elprofe.org/cursos y luego https://elprofe.org/sobre-nosotros, para el servidor son dos peticiones de dos clientes completamente desconocidos.
Esto es como un bar donde el barman sufre de amnesia perfecta: cada vez que te acercas, es la primera vez que te ve. No recuerda que pediste una cerveza hace cinco minutos. Tienes que volver a presentarte desde cero.
¿Suena inútil? En realidad, esta característica hace a HTTP increíblemente escalable. Cualquier servidor puede responder cualquier petición sin necesitar conocer el historial del cliente. Por eso necesitamos mecanismos adicionales —cookies y sesiones— para "simular" memoria y mantener a los usuarios autenticados.
URI vs URL¶
Cuando escribes https://elprofe.org/cursos/javascript?nivel=basico#introduccion estás usando una URL (Uniform Resource Locator), que es un tipo específico de URI (Uniform Resource Identifier, RFC 3986). La URI es el concepto general (cualquier identificador de un recurso); la URL es la URI que además indica cómo acceder al recurso (el protocolo). En la práctica cotidiana, "URL" y "URI" se usan indistintamente, aunque los puristas los distinguen.
6.3 Anatomía de una petición HTTP¶
Analicemos una petición HTTP real, línea por línea. Esta es la petición que tu navegador enviaría al visitar el área de cursos de JavaScript en elprofe.org:
GET /cursos/javascript HTTP/1.1
Host: elprofe.org
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-MX,es;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cache-Control: max-age=0
Cookie: session_id=abc123xyz; preferencias=tema_oscuro
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Referer: https://elprofe.org/inicio
Origin: https://elprofe.org
┌──────────────────────────────────────────────────────────────────────────┐
│ ANATOMÍA DE UNA PETICIÓN HTTP │
├──────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ GET /cursos/javascript HTTP/1.1 ← Request Line │ │
│ │ Host: elprofe.org ← Header obligatorio │ │
│ │ User-Agent: Mozilla/5.0 ... ← Quién soy │ │
│ │ Accept: text/html,... ← Qué formatos acepto │ │
│ │ Accept-Language: es-MX,... ← Idioma preferido │ │
│ │ Accept-Encoding: gzip, br ← Compresión aceptada │ │
│ │ Cache-Control: max-age=0 ← Instrucciones de caché │ │
│ │ Cookie: session_id=abc123xyz ← Cookies del dominio │ │
│ │ Authorization: Bearer eyJ... ← Token de autenticación │ │
│ │ Referer: https://elprofe.org/inicio ← De dónde vengo │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ (línea en blanco) ← Separador headers/body │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ [body vacío — en GET no hay body] │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
Desglose línea por línea¶
GET /cursos/javascript HTTP/1.1 — La Request Line. Tres partes: el método (GET), la URI del recurso (/cursos/javascript), y la versión del protocolo. Esta línea dice todo lo esencial: "quiero leer el recurso /cursos/javascript usando las reglas de HTTP/1.1".
Host: elprofe.org — Obligatorio desde HTTP/1.1. Permite que un servidor con una sola IP sirva cientos de dominios diferentes (virtual hosting). Sin este header, el servidor no sabría qué sitio estás pidiendo. Es tan importante que si lo omites, muchos servidores devuelven un 400 Bad Request.
User-Agent — La "tarjeta de presentación" del cliente. Identifica el navegador y el sistema operativo. Los servidores pueden usar esta información para servir contenido diferente a móviles vs escritorio, o para registrar estadísticas. También lo usan los bots y los rastreadores (¡los servidores pueden detectar si eres un humano o un robot!).
Accept — Le dice al servidor qué tipos de contenido puede procesar el cliente. El valor q=0.9 es el factor de calidad: cuánto prefiere ese formato (1.0 es máxima preferencia). */*;q=0.8 significa "acepto cualquier cosa, pero con menor preferencia".
Accept-Language — El idioma preferido del usuario. Los servidores pueden usar esto para redirigir a es.elprofe.org o servir el contenido en español directamente.
Accept-Encoding — ¿Qué algoritmos de compresión acepta el cliente? gzip (el clásico), deflate, br (Brotli, más eficiente que gzip). Si el servidor lo soporta, comprimirá la respuesta y el navegador la descomprimirá transparentemente.
Cache-Control: max-age=0 — En una petición, este header le dice al servidor/proxy "no me des una copia del caché anterior, dame algo fresco". El navegador envía esto cuando el usuario hace Ctrl+F5.
Cookie — Todas las cookies que el navegador tiene almacenadas para este dominio, concatenadas. El servidor las lee para identificar la sesión del usuario.
Authorization: Bearer eyJ... — El token de autenticación. Bearer indica que es un token opaco (generalmente un JWT). Basic es otra forma: Basic base64(usuario:contraseña). Nunca envíes Authorization por HTTP puro —siempre necesita HTTPS.
Referer — La URL de la página desde la que se hizo la petición. Útil para análisis de tráfico. Nota histórica: la palabra correcta en inglés es "referrer" (con doble r), pero la especificación original de HTTP cometió el error tipográfico y quedó como "Referer" para siempre.
Origin — Similar al Referer, pero solo incluye el esquema + host + puerto, sin la ruta. Es fundamental para el mecanismo CORS (Cross-Origin Resource Sharing). El navegador lo incluye en peticiones cross-origin.
Content-Type y Content-Length — Estos aparecen en peticiones con body (POST, PUT, PATCH). Content-Type: application/json le dice al servidor que el body es JSON. Content-Length: 47 indica cuántos bytes tiene el body.
6.4 Métodos HTTP¶
Los métodos HTTP son los "verbos" del protocolo: definen qué acción quieres realizar sobre un recurso. Antes de ver cada uno, es importante entender dos conceptos:
- Idempotente: ejecutar la operación múltiples veces produce el mismo resultado que ejecutarla una vez. Borrar un archivo dos veces tiene el mismo resultado que borrarlo una: el archivo no existe.
- Seguro: la operación no modifica el estado del servidor. Leer es seguro; borrar no lo es.
GET — Leer un recurso¶
El método más usado en la web. Recupera un recurso sin modificar nada. Es seguro e idempotente.
- Los parámetros van en la URL:
GET /api/cursos?categoria=javascript&nivel=basico - Nunca uses GET para operaciones que modifican datos (el navegador precarga URLs, los buscadores las indexan, los logs las registran con todos los parámetros visibles)
- No tiene body de petición (técnicamente permitido, pero ignorado por convención)
POST — Crear o enviar datos¶
Envía datos al servidor para crear un nuevo recurso o iniciar un proceso. No es idempotente: dos POST iguales generalmente crean dos recursos.
POST /api/usuarios HTTP/1.1
Host: elprofe.org
Content-Type: application/json
Content-Length: 47
{"nombre": "Ana García", "email": "ana@correo.com"}
PUT — Reemplazar completamente¶
Reemplaza el recurso completo con los datos enviados. Es idempotente: hacer PUT con los mismos datos 10 veces deja el recurso en el mismo estado que hacerlo una vez.
PUT /api/usuarios/42 HTTP/1.1
Content-Type: application/json
{"nombre": "Ana García", "email": "ana.nueva@correo.com", "rol": "estudiante"}
PATCH — Actualización parcial¶
Solo envía los campos que cambian. Más eficiente que PUT cuando solo quieres modificar un campo.
DELETE — Eliminar¶
Elimina el recurso identificado por la URI. Es idempotente: eliminar un recurso que ya fue eliminado devuelve 404, pero el estado final es el mismo (el recurso no existe).
HEAD — Como GET, pero sin body¶
El servidor responde con los mismos headers que daría a un GET, pero sin el cuerpo de la respuesta. Útil para verificar si un recurso existe, cuándo fue modificado, o cuál es su tamaño —sin descargar el contenido.
OPTIONS — ¿Qué puedes hacer aquí?¶
Pregunta qué métodos acepta el servidor para ese endpoint. Es la base del mecanismo CORS preflight: antes de hacer una petición cross-origin "peligrosa" (POST, PUT, DELETE), el navegador envía un OPTIONS para preguntar si está permitido.
TRACE y CONNECT¶
TRACE repite la petición tal como la recibe el servidor —útil para diagnóstico pero peligroso en producción (puede revelar headers confidenciales). La mayoría de los servidores lo deshabilitan. CONNECT establece un túnel TCP a través de un proxy, necesario para que los proxies soporten HTTPS.
Tabla comparativa de métodos¶
| Método | Seguro | Idempotente | Body petición | Body respuesta | Uso típico |
|---|---|---|---|---|---|
| GET | ✓ | ✓ | No | Sí | Leer un recurso |
| POST | ✗ | ✗ | Sí | Sí | Crear recurso, enviar formulario |
| PUT | ✗ | ✓ | Sí | Opcional | Reemplazar recurso completo |
| PATCH | ✗ | ✗* | Sí | Opcional | Actualización parcial |
| DELETE | ✗ | ✓ | Opcional | Opcional | Eliminar recurso |
| HEAD | ✓ | ✓ | No | No | Verificar existencia/metadatos |
| OPTIONS | ✓ | ✓ | Opcional | Sí | CORS preflight, descubrir métodos |
| CONNECT | ✗ | ✗ | Especial | Especial | Túnel TCP (proxies HTTPS) |
* PATCH puede ser idempotente según la implementación
6.5 Anatomía de una respuesta HTTP¶
El servidor procesa la petición y responde. Aquí está una respuesta típica de elprofe.org servida desde Cloudflare Pages:
HTTP/2 200 OK
content-type: text/html; charset=utf-8
content-length: 18432
content-encoding: br
cache-control: public, max-age=3600
etag: "a1b2c3d4e5f6"
last-modified: Sun, 28 Jun 2026 10:00:00 GMT
server: cloudflare
date: Sun, 28 Jun 2026 19:33:00 GMT
cf-ray: 8a1b2c3d4e5f6789-MEX
strict-transport-security: max-age=31536000; includeSubDomains
x-content-type-options: nosniff
set-cookie: preferencias=tema_oscuro; Path=/; HttpOnly; Secure; SameSite=Lax
[body: 18432 bytes de HTML comprimido con Brotli]
┌──────────────────────────────────────────────────────────────────────────┐
│ ANATOMÍA DE UNA RESPUESTA HTTP │
├──────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ HTTP/2 200 OK ← Status Line │ │
│ │ content-type: text/html; charset=utf-8 ← Tipo de contenido │ │
│ │ content-length: 18432 ← Tamaño del body │ │
│ │ content-encoding: br ← Compresión Brotli │ │
│ │ cache-control: public, max-age=3600 ← Política de caché │ │
│ │ etag: "a1b2c3d4e5f6" ← Huella del recurso │ │
│ │ last-modified: Sun, 28 Jun 2026 ... ← Última modificación │ │
│ │ strict-transport-security: max-age=... ← Fuerza HTTPS │ │
│ │ set-cookie: preferencias=... ← Establece cookie │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ (línea en blanco) ← Separador │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ [18432 bytes de HTML comprimido con Brotli] │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
Desglose de la respuesta¶
HTTP/2 200 OK — La Status Line. Versión del protocolo, código de estado numérico y frase de razón (la frase es solo para humanos; el código es el que importa).
content-type: text/html; charset=utf-8 — El tipo MIME del contenido. text/html dice que es HTML; charset=utf-8 dice que usa codificación UTF-8. Sin este header, el navegador tiene que adivinar el formato —lo que puede causar problemas de visualización. Otros tipos comunes: application/json, image/png, application/pdf, font/woff2.
content-encoding: br — La respuesta está comprimida con Brotli (br), el algoritmo de Google que supera a gzip en un 15-25% de compresión. El navegador la descomprime transparentemente. Alternativas: gzip, deflate, zstd (Zstandard, cada vez más popular).
cache-control: public, max-age=3600 — Esta respuesta puede almacenarse en cachés públicos (CDN, proxies) y en el navegador, y es válida por 3600 segundos (1 hora). Ver sección 6.9 para el detalle.
etag: "a1b2c3d4e5f6" — Una "huella digital" del recurso. Si el contenido cambia, el ETag cambia. El navegador usa este valor para validar su copia en caché.
last-modified — Cuándo fue modificado el recurso por última vez. Alternativa al ETag para validación basada en fechas.
server: cloudflare — Qué software sirve la respuesta. Buena práctica: ocultar o generalizar este header en producción. No le digas al atacante exactamente qué versión de qué software usas.
cf-ray — Un header personalizado de Cloudflare que identifica el request de forma única. Extremadamente útil para diagnóstico: si algo falla, puedes dar este ID al soporte de Cloudflare y encontrarán exactamente qué pasó.
strict-transport-security: max-age=31536000; includeSubDomains — HSTS: le dice al navegador "durante los próximos 365 días, siempre usa HTTPS para este dominio, aunque el usuario escriba http://". Previene ataques de downgrade (SSL stripping).
x-content-type-options: nosniff — Le dice al navegador "no intentes adivinar el Content-Type; confía en lo que te digo". Previene ataques de MIME sniffing donde un atacante sube un archivo que el navegador malinterpreta como JavaScript ejecutable.
set-cookie — Establece una cookie en el navegador. Ver sección 6.7 para el análisis completo de cookies.
location — Aparece en respuestas 3xx (redirecciones). Indica adónde debe ir el cliente. Ejemplo: location: https://elprofe.org/nueva-url.
6.6 Códigos de estado HTTP¶
Los códigos de estado son el semáforo de Internet: tres dígitos que te dicen exactamente qué pasó con tu petición. El primer dígito define la clase. Están definidos en la RFC 9110 (junio 2022).
1xx — Informativos¶
Raramente visibles para el usuario final; son mensajes de protocolo.
- 100 Continue: el servidor recibió los headers y el cliente puede enviar el body. Se usa cuando el body es grande y el cliente quiere verificar primero que el servidor lo aceptará.
- 101 Switching Protocols: el servidor acepta el upgrade del protocolo. Usado al establecer una conexión WebSocket: el cliente pide
Upgrade: websockety el servidor responde 101.
2xx — Éxito¶
Todo salió bien.
- 200 OK: la respuesta más común. La petición fue exitosa y la respuesta contiene el recurso solicitado.
- 201 Created: se creó un nuevo recurso exitosamente (respuesta típica a un POST). Debe incluir el header
Locationcon la URL del recurso creado. - 202 Accepted: la petición fue aceptada pero el procesamiento aún no terminó (operaciones asíncronas). "Lo tenemos, lo estamos procesando."
- 204 No Content: éxito, pero no hay body que devolver. Típico de DELETE o de PUT que no devuelve el recurso actualizado.
3xx — Redirección¶
El cliente necesita hacer algo más para completar la petición.
- 301 Moved Permanently: el recurso cambió de URL para siempre. Los buscadores actualizan su índice. El SEO se transfiere a la nueva URL. El navegador recuerda la redirección y no volverá a preguntar.
- 302 Found: redirección temporal. El buscador NO actualiza el índice. Úsalo cuando el recurso volverá a su URL original.
- 304 Not Modified: el recurso no cambió desde la última vez que lo tuviste. No hay body —el cliente usa su copia en caché. Ahorra ancho de banda masivamente.
- 307 Temporary Redirect: como el 302 pero garantiza que el método HTTP se preserva. Un POST a una URL que devuelve 307 hace que el cliente repita el POST a la nueva URL (con 302, algunos clientes lo cambian a GET).
- 308 Permanent Redirect: como el 301 pero preserva el método HTTP.
4xx — Error del cliente¶
La petición tiene algo mal. El problema está del lado del que envió la petición.
- 400 Bad Request: la petición está malformada. JSON inválido, parámetros con formato incorrecto, encoding roto.
- 401 Unauthorized: paradójicamente, significa "no autenticado" (no identificado). Necesitas iniciar sesión. El nombre es confuso históricamente.
- 403 Forbidden: estás autenticado, pero no tienes permiso. La diferencia con 401: el servidor sabe quién eres, y la respuesta es "no".
- 404 Not Found: el recurso no existe en esa URL —o el servidor no quiere revelar que existe (a veces se usa 404 en lugar de 403 para no confirmar la existencia de recursos privados).
- 405 Method Not Allowed: ese endpoint no acepta ese método. Mandaste POST a algo que solo acepta GET.
- 408 Request Timeout: el cliente tardó demasiado en enviar la petición completa.
- 409 Conflict: conflicto con el estado actual del recurso. Intentas crear un usuario con un email que ya existe, o hacer una edición sobre una versión obsoleta del documento.
- 429 Too Many Requests (RFC 6585): rate limiting. Hiciste demasiadas peticiones en poco tiempo. El servidor puede incluir
Retry-After: 60para indicar cuántos segundos esperar.
5xx — Error del servidor¶
Algo falló en el servidor. El cliente hizo todo bien.
- 500 Internal Server Error: algo salió mal en el servidor. Ve a los logs.
- 501 Not Implemented: el servidor no implementa el método solicitado.
- 502 Bad Gateway: el servidor actuó como proxy y recibió una respuesta inválida del servidor de origen. Común cuando Nginx no puede comunicarse con tu aplicación Node.js o Python.
- 503 Service Unavailable: el servidor está caído por mantenimiento o sobrecarga. Incluir
Retry-Afteres buena práctica. - 504 Gateway Timeout: el proxy esperó demasiado al servidor de origen. Tu aplicación tardó más que el timeout configurado.
Tabla resumen de códigos¶
| Código | Nombre | Clase | Causa común | Acción típica |
|---|---|---|---|---|
| 200 | OK | Éxito | Petición exitosa | Procesar el body |
| 201 | Created | Éxito | POST exitoso | Leer el header Location |
| 204 | No Content | Éxito | DELETE/PUT sin respuesta | No esperar body |
| 301 | Moved Permanently | Redirección | URL cambió definitivamente | Actualizar bookmarks/links |
| 304 | Not Modified | Redirección | Caché válido | Usar copia local |
| 400 | Bad Request | Error cliente | Datos malformados | Revisar formato de la petición |
| 401 | Unauthorized | Error cliente | Sin autenticación | Iniciar sesión / incluir token |
| 403 | Forbidden | Error cliente | Sin permisos | Solicitar acceso |
| 404 | Not Found | Error cliente | URL no existe | Verificar la URL |
| 429 | Too Many Requests | Error cliente | Rate limiting | Esperar y reintentar |
| 500 | Internal Server Error | Error servidor | Excepción en el código | Revisar logs del servidor |
| 502 | Bad Gateway | Error servidor | Proxy sin respuesta del origen | Verificar app/servicio de origen |
| 503 | Service Unavailable | Error servidor | Servidor caído o sobrecargado | Reintentar después (Retry-After) |
6.7 Cookies: el truco para simular memoria¶
Recuerda la analogía del barman con amnesia: HTTP no recuerda nada entre peticiones. Pero la web moderna necesita saber quién eres: que iniciaste sesión, que tienes un carrito de compras, que prefieres el tema oscuro. ¿Cómo resolver esto si el protocolo no tiene memoria?
En 1994, Lou Montulli, un ingeniero de Netscape, inventó las cookies. La solución es elegante: si el servidor no puede recordar al cliente, que el cliente se "lleve" la información consigo y la presente en cada visita. Como un sello en la mano que te ponen al entrar a una discoteca: el servidor te da el sello y tú se lo muestras en cada petición para demostrar quién eres.
Cómo funcionan¶
┌───────────────────────────────────────────────────────────────────────────┐
│ FLUJO DE COOKIES │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ NAVEGADOR SERVIDOR (elprofe.org) │
│ │ │ │
│ │ 1. GET /login (sin cookie) │ │
│ │ ─────────────────────────────────────► │ │
│ │ │ │
│ │ 2. POST /login {usuario, contraseña} │ │
│ │ ─────────────────────────────────────► │ ← Verifica credenciales │
│ │ │ │
│ │ 3. 200 OK │ │
│ │ Set-Cookie: session=abc123; │ │
│ │ HttpOnly; Secure; SameSite=Lax │ │
│ │ ◄───────────────────────────────────── │ │
│ │ │ │
│ │ [navegador guarda la cookie] │ │
│ │ │ │
│ │ 4. GET /cursos │ │
│ │ Cookie: session=abc123 │ │
│ │ ─────────────────────────────────────► │ ← Busca sesión en BD │
│ │ │ │
│ │ 5. 200 OK [página de cursos] │ │
│ │ ◄───────────────────────────────────── │ │
│ │
└───────────────────────────────────────────────────────────────────────────┘
Tipos de cookies¶
Cookies de sesión: no tienen Expires ni Max-Age. Viven en la memoria del navegador y desaparecen cuando el usuario cierra el navegador (o la pestaña, según la implementación).
Cookies persistentes: tienen Expires (fecha absoluta) o Max-Age (segundos desde ahora). Sobreviven al cierre del navegador. Útiles para el "recuérdame" en el inicio de sesión.
Atributos de seguridad¶
Aquí está el núcleo de las cookies bien implementadas:
Secure: la cookie solo se envía sobre HTTPS. Nunca sobre HTTP puro. Esencial en producción —sin este atributo, la cookie vuela en texto plano por la red.
HttpOnly: JavaScript no puede leer esta cookie con document.cookie. El navegador la envía automáticamente en peticiones HTTP, pero los scripts no pueden acceder a ella. Esto previene el robo de cookies mediante ataques XSS (Cross-Site Scripting): aunque un atacante inyecte JavaScript malicioso en tu página, no podrá robar la cookie de sesión.
SameSite: controla cuándo se envía la cookie en peticiones cross-site (desde otro dominio):
- SameSite=Strict: la cookie nunca se envía en peticiones originadas desde otro sitio. Máxima protección contra CSRF, pero puede romper flujos legítimos (ej: cuando llegas desde un email).
- SameSite=Lax: se envía en navegación top-level (hacer clic en un enlace) pero no en sub-requests cross-site (imágenes, iframes, fetch). Es el valor predeterminado en navegadores modernos. Buen equilibrio entre seguridad y usabilidad.
- SameSite=None; Secure: se envía siempre, incluso en contextos cross-site. Requerido para pagos integrados, iframes de terceros, SSO. El atributo Secure es obligatorio con None.
Ejemplo real: la cookie de sesión de elprofe.org debe configurarse así:
6.8 Sesiones¶
Hay una confusión frecuente entre "cookie" y "sesión". Son conceptos distintos aunque relacionados:
- Cookie: el mecanismo para guardar datos en el navegador del usuario. Los datos viven en el cliente.
- Sesión: los datos de estado del usuario guardados en el servidor. La cookie solo lleva el identificador de sesión (un ID aleatorio). Los datos reales están en el servidor.
La distinción es importante por seguridad: si guardas {"usuario": "Ana", "rol": "admin"} directamente en una cookie, el usuario podría modificar ese valor. Si guardas solo un ID de sesión opaco (session_id=xyz789), el servidor es quien tiene los datos reales y el usuario no puede manipularlos.
Flujo de autenticación con sesiones¶
┌──────────────────────────────────────────────────────────────────────────┐
│ FLUJO DE SESIÓN COMPLETO │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ NAVEGADOR SERVIDOR BASE DE DATOS │
│ │ │ │ │
│ │ POST /login │ │ │
│ │ {user, pass} │ │ │
│ │ ────────────────► │ │ │
│ │ │ SELECT * WHERE │ │
│ │ │ email=user │ │
│ │ │ ────────────────────►│ │
│ │ │ ◄──────────────── usuario+hash ── │
│ │ │ [verifica bcrypt] │ │
│ │ │ INSERT sessions │ │
│ │ │ (id=xyz, user=Ana) │ │
│ │ │ ────────────────────►│ │
│ │ 200 OK │ │ │
│ │ Set-Cookie: │ │ │
│ │ session_id=xyz │ │ │
│ │◄──────────────────│ │ │
│ │ │ │ │
│ │ GET /dashboard │ │ │
│ │ Cookie: id=xyz │ │ │
│ │ ────────────────► │ │ │
│ │ │ SELECT WHERE id=xyz │ │
│ │ │ ────────────────────►│ │
│ │ │ ◄──── sesión de Ana ─│ │
│ │ 200 OK [dashboard]│ │ │
│ │◄──────────────────│ │ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
La alternativa moderna: JWT¶
Los JSON Web Tokens (JWT) invierten el modelo: en lugar de guardar la sesión en el servidor, el servidor firma un token que contiene los datos del usuario (sin contraseñas) y se lo da al cliente. El cliente lo envía en cada petición con Authorization: Bearer eyJ.... El servidor verifica la firma criptográfica —si es válida, confía en los datos del token.
Ventaja: sin estado en el servidor, escala perfectamente. Desventaja: revocar un JWT antes de que expire es complejo. Ambos enfoques tienen su lugar, y muchas aplicaciones modernas los combinan.
6.9 Caché HTTP: no pedir lo que ya tienes¶
El caché HTTP es uno de los mecanismos más poderosos —y más mal configurados— de la web. La analogía perfecta: el caché es como la memoria RAM de tu computadora. En lugar de ir al disco duro (el servidor de origen) cada vez que necesitas un dato, primero revisas la RAM (el caché). Si el dato está ahí y sigue siendo válido, perfecto. Si no, vas al disco.
Para elprofe.org, una imagen del logo podría servirse millones de veces al mes. Con caché bien configurado, el servidor solo la sirve una vez al navegador de cada usuario; después, el navegador la carga de su almacenamiento local en microsegundos. Sin caché, cada visita descarga la imagen de nuevo: más tiempo, más ancho de banda, más carga en el servidor.
Directivas Cache-Control (RFC 9111)¶
El header Cache-Control es la forma moderna de controlar el caché (reemplaza al antiguo Expires):
max-age=3600: este recurso es válido por 3600 segundos (1 hora). Después, el cliente debe revalidarlo.no-cache: no uses la copia sin antes preguntar al servidor si sigue siendo válida. Si el servidor dice "sí, sigue igual" (304 Not Modified), el cliente puede usar su copia. Confuso nombre: no significa "no uses caché".no-store: nunca almacenes este recurso en ningún caché, en ningún lugar. Para datos ultra-sensibles: estados bancarios, registros médicos, información confidencial.public: cualquier caché puede almacenarlo: el navegador del usuario, proxies, CDNs. Ideal para assets estáticos públicos.private: solo el navegador del usuario puede guardarlo. Los CDNs y proxies intermedios no. Para contenido personalizado: tu perfil de usuario, tu dashboard.must-revalidate: una vez que elmax-ageexpira, el cliente DEBE revalidar con el servidor antes de usar la copia, incluso si está offline.
ETag: validación inteligente¶
┌──────────────────────────────────────────────────────────────────────────┐
│ FLUJO DE VALIDACIÓN CON ETAG │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ PRIMERA PETICIÓN: │
│ Navegador ──── GET /logo.png ──────────────────────► Servidor │
│ Navegador ◄─── 200 OK + ETag: "abc123" ─────────── Servidor │
│ [descarga 45KB] │
│ │
│ PETICIÓN SIGUIENTE (max-age expirado): │
│ Navegador ──── GET /logo.png ──────────────────────► Servidor │
│ If-None-Match: "abc123" │
│ │
│ SI EL RECURSO NO CAMBIÓ: │
│ Navegador ◄─── 304 Not Modified ──────────────────── Servidor │
│ [0 bytes descargados — ahorro total] │
│ │
│ SI EL RECURSO CAMBIÓ: │
│ Navegador ◄─── 200 OK + ETag: "xyz789" ──────────── Servidor │
│ [descarga los 45KB con el nuevo contenido] │
│ │
└──────────────────────────────────────────────────────────────────────────┘
El ETag es como el número de versión de un documento. El navegador guarda ETag: "abc123" junto al recurso. La próxima vez que necesita el recurso y su max-age expiró, envía If-None-Match: "abc123". El servidor compara: ¿cambió el archivo desde que tenía ese ETag? Si no cambió, responde 304 Not Modified sin body —ahorrando todo el ancho de banda de descarga. Si cambió, responde 200 OK con el nuevo contenido y el nuevo ETag.
Estrategia de caché para elprofe.org¶
assets estáticos (CSS, JS, imágenes, fonts con hash en el nombre):
cache-control: public, max-age=31536000, immutable
→ 1 año de caché. Si el archivo cambia, el nombre cambia (bundle.a1b2c3.js)
HTML dinámico:
cache-control: no-cache, must-revalidate
→ Siempre verificar antes de usar — el HTML referencia los assets con los nombres actuales
API responses (datos del usuario):
cache-control: private, no-cache
→ Solo el navegador del usuario, y siempre verificar
Assets públicos sin versioning:
cache-control: public, max-age=3600
etag: "a1b2c3"
→ 1 hora, con ETag para validación eficiente
6.10 HTTP/2: el mismo idioma, motor nuevo¶
Después de 18 años de servicio fiel, HTTP/1.1 empezó a mostrar sus costuras. El problema central: las páginas web modernas tienen cientos de recursos (JS, CSS, imágenes, fonts, APIs). HTTP/1.1 los sirve uno a uno por conexión, o usa múltiples conexiones paralelas —pero los navegadores limitan esto a 6 conexiones por dominio para no saturar los servidores.
El problema de Head-of-Line Blocking (HOL)¶
Imagina un restaurante de comida rápida con un solo mostrador. Los clientes hacen cola y cada uno pide en orden. Si el primero pide una hamburguesa especial que tarda 10 minutos, todos los demás esperan aunque sus pedidos fueran instantáneos. Eso es HOL blocking en HTTP/1.1.
Con pipelining (enviar varias peticiones sin esperar respuestas), el problema mejora levemente, pero si la primera respuesta es lenta, bloquea todas las demás. En la práctica, el pipelining fue tan difícil de implementar correctamente que nunca se adoptó ampliamente.
Las soluciones de HTTP/2¶
Basado en SPDY de Google, la RFC 9113 (HTTP/2) resolvió estos problemas con un enfoque radicalmente diferente:
Binary framing: En lugar de texto plano (GET /ruta HTTP/1.1\r\nHost: ...), HTTP/2 divide todo en pequeños frames binarios. Un frame tiene un tipo (HEADERS, DATA, SETTINGS, etc.), un Stream ID, y el contenido. El parsing binario es más eficiente y menos propenso a errores que parsear texto.
Multiplexación: Múltiples streams (petición+respuesta) viajan simultáneamente en una sola conexión TCP. El Stream 1 puede estar descargando el HTML mientras el Stream 3 descarga un JS y el Stream 5 descarga una imagen. Si el Stream 3 es lento, los demás no se ven afectados —a nivel de aplicación.
Compresión de headers (HPACK): Los headers HTTP se repiten enormemente entre peticiones. En HTTP/1.1, enviar User-Agent: Mozilla/5.0... en cada una de las 50 peticiones de una página es un desperdicio. HPACK mantiene una tabla de headers ya enviados y los referencia con un número. En lugar de 200 bytes de User-Agent, envías 1 byte.
Stream prioritization: El cliente puede indicar qué streams son más urgentes. "Dame el CSS antes que las imágenes, porque lo necesito para renderizar".
┌─────────────────────────────────────────────────────────────────────────┐
│ HTTP/1.1 vs HTTP/2 — CONEXIONES Y STREAMS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ HTTP/1.1 (6 conexiones paralelas): │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Conexión 1 │ │ Conexión 2 │ │ Conexión 3 │ ... │
│ │ GET /1.js │ │ GET /2.css │ │ GET /3.png │ │
│ │ [.... │ │ [.... │ │ [.... │ │
│ │ .... │ │ .... │ │ .... │ │
│ │ ....] │ │ ....] │ │ ....] │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ HTTP/2 (1 conexión, múltiples streams): │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Conexión TCP única │ │
│ │ Stream 1: ─HEADERS─DATA─DATA─DATA─DATA─DATA─► │ │
│ │ Stream 3: ─HEADERS─DATA─DATA─► │ │
│ │ Stream 5: ─HEADERS─DATA─DATA─DATA─► │ │
│ │ Stream 7: ─HEADERS─DATA─► │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ Resultado: menos overhead TCP, mejor uso del ancho de banda │
└─────────────────────────────────────────────────────────────────────────┘
La limitación que queda¶
HTTP/2 eliminó el HOL blocking a nivel de aplicación, pero sigue usando TCP. Y TCP garantiza la entrega ordenada de paquetes: si un paquete se pierde, el sistema operativo detiene toda la conexión hasta que ese paquete llega de nuevo. Con HTTP/1.1 y 6 conexiones, perder un paquete afectaba 1/6 de los streams. Con HTTP/2 y una sola conexión, perder un paquete afecta todos los streams. En redes con pérdida de paquetes, HTTP/2 puede ser peor que HTTP/1.1.
A pesar de esto, HTTP/2 es el protocolo más usado: 51.08% de requests globales según Cloudflare Radar en mayo de 2026.
6.11 HTTP/3: la revolución sobre UDP¶
La solución al HOL blocking de TCP fue radical: abandonar TCP por completo. Google desarrolló internamente el protocolo QUIC en 2012. El IETF lo estandarizó como RFC 9000 en mayo de 2021. HTTP/3 (RFC 9114, junio 2022) es HTTP sobre QUIC.
¿Qué es QUIC?¶
QUIC es un protocolo de transporte que corre sobre UDP en lugar de TCP. UDP es el protocolo "sin garantías": envía paquetes y no se preocupa por si llegaron ni en qué orden. Esto suena peor, pero QUIC implementa él mismo toda la confiabilidad —y lo hace de una manera más inteligente que TCP.
Las características clave de QUIC:
Cifrado TLS 1.3 integrado: En TCP+TLS, el cifrado es una capa adicional. En QUIC, TLS 1.3 está construido dentro del protocolo. No puedes usar QUIC sin cifrado. Esto es excelente para la seguridad.
0-RTT para conexiones conocidas: En TCP, establecer una conexión requiere 1 RTT (round-trip time) solo para el handshake TCP, más 1 RTT para TLS 1.3 = 2 RTT antes de enviar el primer byte de datos. QUIC combina la negociación de transporte y cifrado en 1 RTT para conexiones nuevas. Para conexiones conocidas (el servidor recuerda al cliente), puede ser 0 RTT: el cliente puede enviar datos en el primer paquete.
Streams completamente independientes: A diferencia de TCP, donde todos los streams comparten una sola cola de ordenación, en QUIC cada stream es verdaderamente independiente. Si un paquete del stream A se pierde, solo ese stream espera. Los streams B, C y D siguen fluyendo. HOL blocking eliminado completamente.
Connection migration: Los IDs de conexión en QUIC son independientes de la IP y el puerto. Si cambias de Wi-Fi a 4G mientras descargas un video, la conexión continúa sin interrupción. En TCP, cambiar de IP rompe la conexión.
┌──────────────────────────────────────────────────────────────────────────┐
│ HTTP/2 (TCP+TLS) vs HTTP/3 (QUIC+TLS) — CONEXIÓN INICIAL │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ HTTP/2 (nueva conexión): │
│ Cliente ──SYN──────────────────────────────────────► Servidor │
│ Cliente ◄──────────────────────────────────SYN+ACK── Servidor │
│ Cliente ──ACK──────────────────────────────────────► Servidor [1 RTT] │
│ Cliente ──ClientHello (TLS)────────────────────────► Servidor │
│ Cliente ◄──────────────────────────ServerHello+Cert── Servidor [2 RTT] │
│ Cliente ──Finished──────────────────────────────────► Servidor │
│ │
│ [Primer byte de datos HTTP/2 llega aquí → 2 RTT mínimo] │
│ │
│ HTTP/3 QUIC (nueva conexión): │
│ Cliente ──Initial (QUIC+TLS ClientHello)───────────► Servidor │
│ Cliente ◄──Handshake (ServerHello+Cert+Finished)───── Servidor [1 RTT] │
│ Cliente ──Handshake (Finished) + HTTP data ────────► Servidor │
│ │
│ [Primer byte de datos llega aquí → 1 RTT] │
│ │
│ HTTP/3 QUIC (conexión conocida, 0-RTT): │
│ Cliente ──Initial + HTTP data ────────────────────► Servidor [0 RTT!] │
│ │
└──────────────────────────────────────────────────────────────────────────┘
Datos reales de adopción¶
Los números hablan por sí solos. Según W3Techs (junio 2026) y Cloudflare Radar (mayo 2026):
- HTTP/3 es soportado por el 39.8% de los sitios web a nivel mundial.
- El 21.20% de requests globales ya usa HTTP/3.
- En los top 1,000 sitios del mundo, HTTP/3 supera el 50% de adopción.
- Cloudflare tiene HTTP/3 habilitado en el 98% de sus sitios por defecto.
- HTTP/3 mejora el TTFB (Time To First Byte) en aproximadamente 180ms en mobile comparado con HTTP/2 en primera conexión.
- En conexiones 4G con 15% de pérdida de paquetes, HTTP/3 mejora la carga de página hasta un 55% versus HTTP/2 (Internet Society).
QUIC y HTTP/3 son especialmente valiosos en redes móviles y de alta latencia, donde la pérdida de paquetes es común. Para elprofe.org servido desde Cloudflare Pages, HTTP/3 se activa automáticamente —tus usuarios lo usan sin que hagas nada.
6.12 HTTPS: cuando la privacidad se volvió obligatoria¶
HTTP puro es como una postal: cualquier persona que la maneje entre el remitente y el destinatario puede leer el mensaje. En una red local, en un café con Wi-Fi público, en el ISP, en un servidor intermedio —cualquiera puede ver exactamente lo que envías y recibes. Tus contraseñas, tu número de tarjeta de crédito, tus mensajes privados: todo en texto plano.
Los tres problemas de HTTP sin cifrar¶
Intercepción (Man-in-the-Middle): Un atacante en la misma red puede capturar toda la conversación. Con Wireshark en un Wi-Fi público, es trivial ver las credenciales de quien inicia sesión en un sitio HTTP.
Modificación en tránsito: No solo pueden leer los datos —pueden modificarlos. Algunos ISPs inyectaban anuncios en páginas HTTP. Atacantes más maliciosos pueden inyectar malware en el JavaScript descargado.
Suplantación: ¿Cómo sabes que el servidor que responde realmente es elprofe.org y no un impostor que interceptó la conexión? Con HTTP puro, no puedes saberlo.
Los tres pilares de HTTPS¶
HTTPS = HTTP + TLS (Transport Layer Security). TLS resuelve los tres problemas:
- Confidencialidad: los datos viajan cifrados con algoritmos modernos (AES-256, ChaCha20). Solo el cliente y el servidor pueden descifrarlos.
- Integridad: cada mensaje incluye un código de autenticación (HMAC). Si alguien modifica un solo bit en tránsito, la verificación falla y la conexión se rompe.
- Autenticación: el servidor presenta un certificado digital firmado por una Autoridad Certificadora (CA) de confianza. El navegador verifica la firma. Si el certificado dice "esto es elprofe.org" y la CA lo respalda, el navegador muestra el candado.
┌──────────────────────────────────────────────────────────────────────────┐
│ HTTP vs HTTPS — QUÉ VE UN ATACANTE EN LA RED │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ HTTP (sin cifrar): │
│ ┌────────────┐ ┌──────────────────────┐ │
│ │ Navegador │ ←─── TEXTO PLANO ──────────► │ Servidor │ │
│ └────────────┘ └──────────────────────┘ │
│ ↑ │
│ ATACANTE VE: │
│ "POST /login │
│ usuario=ana&password=Secreto123" │
│ │
│ HTTPS (con TLS): │
│ ┌────────────┐ ┌──────────────────────┐ │
│ │ Navegador │ ←── DATOS CIFRADOS ────────► │ Servidor │ │
│ └────────────┘ └──────────────────────┘ │
│ ↑ │
│ ATACANTE VE: │
│ "x9#Km2$pL@qR7vN..." │
│ (inútil sin la clave privada) │
│ │
└──────────────────────────────────────────────────────────────────────────┘
La transición obligatoria¶
El cambio de HTTP a HTTPS como estándar fue gradual pero decisivo:
- 2014: Google anuncia que HTTPS es un factor de posicionamiento en búsquedas (SEO).
- 2016: Let's Encrypt lanza su versicio de certificados gratuitos y automatizados. La excusa "los certificados son caros" desaparece.
- 2018: Chrome 68 marca todos los sitios HTTP como "No seguro" en la barra de direcciones.
- 2023: la mayoría de navegadores bloquean por defecto los recursos mixtos (cargar una imagen HTTP dentro de una página HTTPS).
- 2026: el 90.1% de los sitios web usa HTTPS por defecto.
HSTS: no más HTTP accidental¶
Incluso con HTTPS activo, hay un riesgo: la primera vez que un usuario visita tu sitio escribiendo elprofe.org sin el https://, el navegador lo intenta primero por HTTP (puerto 80). Un atacante podría interceptar esa primera petición HTTP y servir una versión falsa del sitio.
La solución es HSTS (HTTP Strict Transport Security). Cuando tu servidor envía:
El navegador "recuerda" por 365 días que este dominio siempre debe usar HTTPS. Cualquier intento de acceder por HTTP se convierte automáticamente en HTTPS en el navegador, antes de enviar cualquier petición. Con preload, incluso la primera visita está protegida (el dominio está en una lista hardcodeada en los navegadores).
Nota: El funcionamiento completo de TLS —el handshake, los certificados, las Autoridades Certificadoras y Let's Encrypt— se explica en detalle en el Capítulo 7.
6.13 Herramientas para analizar HTTP¶
Entender HTTP en teoría es una cosa; verlo en acción es otra completamente diferente. Estas herramientas te permiten espiar las conversaciones HTTP en tiempo real.
DevTools del navegador (Chrome/Firefox)¶
La herramienta más accesible y poderosa para el día a día. Abre con F12 (o Cmd+Option+I en Mac) y ve a la pestaña Network.
Columnas clave: - Status: el código de respuesta (200, 304, 404…) - Type: tipo de recurso (document, script, stylesheet, xhr, fetch, img) - Size: tamaño descargado vs tamaño real (muestra si hay compresión y si viene del caché) - Time: tiempo total de la petición - Protocol: HTTP/1.1, h2 (HTTP/2) o h3 (HTTP/3)
Para analizar elprofe.org paso a paso:
- Abre DevTools → Network
- Activa "Disable cache" (para ver las peticiones reales, no del caché)
- Navega a
https://elprofe.org - Observa la cascada (Waterfall): la petición principal (document) y todos los recursos que carga
- Haz clic en la petición del documento principal → Headers → verás todos los request headers y response headers
- En la columna "Protocol", verifica si usa h2 o h3
- Desactiva "Disable cache" y recarga (Ctrl+R): observa cómo aparecen peticiones con
304 Not Modifiedy cómo "Size" muestra "(disk cache)" —eso es tu caché trabajando
El panel Timing de cada petición muestra: - DNS Lookup: cuánto tardó en resolver el dominio - Initial connection: handshake TCP - SSL: handshake TLS - Waiting (TTFB): tiempo hasta el primer byte de respuesta - Content Download: tiempo de descarga del body
curl¶
La navaja suiza de HTTP en la terminal. Disponible en Linux, macOS y Windows.
```bash
Ver solo los headers de respuesta (sin body)¶
Ver TODO el flujo: petición y respuesta con detalles¶
Hacer un POST con JSON¶
Forzar HTTP/2¶
Forzar HTTP/3 (curl debe estar compilado con soporte QUIC)¶
Seguir redirecciones automáticamente¶
Guardar headers de respuesta en un archivo¶
Ver todos los headers de respuesta del sitio principal¶
Ver el flujo completo (petición + respuesta) con detalles de conexión¶
Ver los headers de respuesta y forzar HTTP/2¶
Probar el redirect de un URL shortener¶
Nota el header "location" en la respuesta¶
Ver si un recurso responde con ETag¶
Anota el valor de etag¶
Hacer una segunda petición con el ETag (simular validación condicional)¶
¿Responde con 304?¶
Ver qué headers de seguridad tiene el sitio¶
Paso 9: Analizar un recurso estático específico¶
Busca en DevTools una petición de un archivo CSS o JS. Analiza sus headers de respuesta específicamente:
cache-control: ¿tienemax-age=31536000, immutable? ¿O algo diferente? ¿Por qué?content-encoding: ¿está comprimido con Brotli o gzip?etag: ¿tiene un ETag? ¿Cómo se ve?content-length: ¿cuántos bytes tiene comprimido? DevTools también muestra el tamaño descomprimido
Calcula la tasa de compresión: tamaño comprimido / tamaño original × 100. Un buen Brotli en JavaScript puede dar 25-35% del tamaño original.
Paso 10: Reporte técnico final¶
Crea un documento (puede ser un archivo de texto, un Notion, o un Google Doc) con los siguientes apartados:
1. Información general del sitio - URL analizada - Fecha y hora del análisis - Protocolo principal usado (HTTP/1.1, H2, H3) - CDN identificado (Cloudflare, AWS CloudFront, Fastly, ninguno)
2. Primera carga (sin caché) - Número total de peticiones - Tamaño total transferido vs tamaño real (ratio de compresión) - Tiempo total de carga - Tiempo de la petición principal (TTFB + descarga)
3. Segunda carga (con caché) - Número de respuestas 304 Not Modified - Número de recursos desde disk/memory cache - Tiempo total de carga (comparar con primera carga) - Ahorro de datos gracias al caché (bytes)
4. Headers de seguridad
- ¿Tiene HSTS? ¿Con qué max-age?
- ¿Tiene X-Content-Type-Options?
- ¿Tiene X-Frame-Options o CSP?
- ¿Tiene cookies con HttpOnly y Secure?
5. Análisis de caché
- Política de caché del HTML principal
- Política de caché de assets estáticos
- ¿Usa ETags? ¿Usa Last-Modified?
- ¿Está optimizada la estrategia de caché? ¿Qué mejorarías?
6. Observaciones y hallazgos
- ¿Qué protocolo usa? ¿HTTP/3?
- ¿Algún recurso tiene headers de caché incorrectos?
- ¿Hay cookies sin HttpOnly?
- ¿Qué mejorarías en la configuración HTTP del sitio?
Este análisis te dará experiencia práctica real que no puedes obtener solo leyendo. Lo que acabas de hacer es exactamente lo que hace un desarrollador senior cuando optimiza el rendimiento o audita la seguridad de un sitio web.
Referencias bibliográficas¶
-
HTTP Semantics — RFC 9110 (junio 2022). Fielding, R. et al. Internet Engineering Task Force (IETF). https://www.rfc-editor.org/rfc/rfc9110
-
HTTP Caching — RFC 9111 (junio 2022). Fielding, R. et al. IETF. https://www.rfc-editor.org/rfc/rfc9111
-
HTTP/1.1 — RFC 9112 (junio 2022). Fielding, R. et al. IETF. https://www.rfc-editor.org/rfc/rfc9112
-
HTTP/2 — RFC 9113 (junio 2022). Thomson, M. y Norris, C. IETF. https://www.rfc-editor.org/rfc/rfc9113
-
HTTP/3 — RFC 9114 (junio 2022). Bishop, M. IETF. https://www.rfc-editor.org/rfc/rfc9114
-
QUIC: A UDP-Based Multiplexed and Secure Transport — RFC 9000 (mayo 2021). Iyengar, J. y Thomson, M. IETF. https://www.rfc-editor.org/rfc/rfc9000
-
HTTP State Management Mechanism (Cookies) — RFC 6265 (abril 2011). Barth, A. IETF. https://www.rfc-editor.org/rfc/rfc6265
-
Additional HTTP Status Codes — RFC 6585 (abril 2012). Nottingham, M. y Fielding, R. IETF. https://www.rfc-editor.org/rfc/rfc6585
-
Usage Statistics and Market Share of Site Elements for Websites (junio 2026). W3Techs. https://w3techs.com/technologies/overview/site_element
-
HTTP Protocol Adoption and HTTP/3 Statistics (2026). TechnologyChecker.io. https://technologychecker.io/blog/http-protocol-adoption
-
HTTP Reference: Status Codes. MDN Web Docs, Mozilla Foundation. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status
-
Research: HTTP/3 and QUIC Adoption 2026. Enterno.io Research. https://enterno.io/en/s/research-http3-quic-adoption-2026
-
HTTP: The Definitive Guide (2002). Gourley, D. y Totty, B. O'Reilly Media. https://www.oreilly.com/library/view/http-the-definitive/1565925092/
-
An overview of HTTP. MDN Web Docs, Mozilla Foundation. https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
-
Uniform Resource Identifier (URI) Generic Syntax — RFC 3986 (enero 2005). Berners-Lee, T. et al. IETF. https://www.rfc-editor.org/rfc/rfc3986
¿Qué sigue? — Capítulo 7: TLS y los certificados SSL¶
En este capítulo aprendiste que HTTPS = HTTP + TLS, y que TLS proporciona confidencialidad, integridad y autenticación. Pero ¿cómo funciona exactamente TLS? ¿Cómo se genera el cifrado sin que nadie intercepte la clave? ¿Qué es un certificado digital y cómo sabe tu navegador que puede confiar en él? ¿Por qué aparece el candado verde? ¿Qué es una CA (Autoridad Certificadora) y cómo funciona la jerarquía de confianza?
En el Capítulo 7: TLS y los certificados SSL — El sistema de confianza en Internet, exploraremos:
- El handshake TLS 1.3 paso a paso: cómo cliente y servidor establecen una conexión cifrada sin haberse visto antes (la "magia" del intercambio de claves Diffie-Hellman)
- Los certificados X.509: qué contienen, cómo se firman, cómo los verifica el navegador
- La cadena de confianza: Root CA → Intermediate CA → certificado del servidor
- Let's Encrypt: cómo obtener certificados gratuitos y automáticos con ACME
- Configuración de certificados en Cloudflare, Firebase y AWS
- Los errores de certificado más comunes: qué significan y cómo resolverlos
- Certificate Transparency: el registro público de todos los certificados emitidos
Si lograste entender HTTP completamente, TLS será el siguiente nivel. Es el sistema de confianza invisible que hace posible toda la web segura.
Fin del Capítulo 6