Cómo funciona HTTP: métodos, cabeceras y códigos de estado

D
DanisCh
(Actualizado: ) • 14 min de lectura
Cómo funciona HTTP: métodos, cabeceras y códigos de estado
HTML Programación

HTTP (HyperText Transfer Protocol) es el protocolo que hace posible la web. Cada vez que tu navegador carga una página, envía una imagen o hace una llamada a una API, usa HTTP. Como desarrollador, entender cómo funciona HTTP no es solo teoría: te ayuda a depurar problemas de red, diseñar mejores APIs, configurar cachés y entender qué pasa realmente cuando algo falla.

En esta guía aprenderás cómo funciona el protocolo, qué son los métodos y cuándo usarlos, cómo leer las cabeceras y qué significan los códigos de estado.

Cómo funciona HTTP: el modelo petición-respuesta

HTTP es un protocolo sin estado basado en peticiones y respuestas. El cliente (generalmente un navegador o una aplicación) envía una petición al servidor, el servidor la procesa y devuelve una respuesta. El servidor no recuerda nada de peticiones anteriores: cada petición es independiente.

# Flujo básico de una petición HTTP

# 1. El cliente abre una conexión TCP con el servidor (puerto 80 para HTTP, 443 para HTTPS)
# 2. El cliente envía una petición HTTP
# 3. El servidor procesa la petición
# 4. El servidor devuelve una respuesta HTTP
# 5. La conexión se cierra (o se reutiliza con HTTP/1.1 keep-alive)

# Ejemplo: qué ocurre cuando escribes "example.com" en el navegador
# 1. DNS: "¿cuál es la IP de example.com?" → 93.184.216.34
# 2. TCP: conectar al puerto 443 de 93.184.216.34
# 3. TLS: handshake para establecer conexión segura (HTTPS)
# 4. HTTP: enviar la petición GET
# 5. HTTP: recibir la respuesta con el HTML
# 6. El navegador parsea el HTML y repite el proceso para CSS, JS, imágenes

 

Anatomía de una petición HTTP

# Una petición HTTP tiene tres partes: línea de petición, cabeceras y cuerpo

# ─── Línea de petición ────────────────────────────────────────────────────────
GET /blog/articulo-123 HTTP/1.1

# GET: método HTTP (qué quiero hacer)
# /blog/articulo-123: ruta del recurso
# HTTP/1.1: versión del protocolo

# ─── Cabeceras ────────────────────────────────────────────────────────────────
Host: www.ejemplo.com              # obligatorio en HTTP/1.1: a qué servidor va
Accept: text/html,application/json # qué formatos acepta el cliente
Accept-Language: es-ES,es;q=0.9   # idioma preferido
Accept-Encoding: gzip, deflate     # compresión aceptada
Authorization: Bearer eyJhbGci... # credenciales de autenticación
Content-Type: application/json     # formato del cuerpo (en POST/PUT/PATCH)
User-Agent: Mozilla/5.0 ...        # identificación del cliente
Cookie: sesion_id=abc123           # cookies

# ─── Línea vacía (obligatoria: separa cabeceras del cuerpo) ──────────────────

# ─── Cuerpo (solo en POST, PUT, PATCH) ───────────────────────────────────────
{
  "nombre": "Ana García",
  "email": "ana@mail.com"
}

 

Anatomía de una respuesta HTTP

# Una respuesta HTTP también tiene tres partes

# ─── Línea de estado ──────────────────────────────────────────────────────────
HTTP/1.1 200 OK

# HTTP/1.1: versión del protocolo
# 200: código de estado numérico
# OK: descripción textual del código (reason phrase)

# ─── Cabeceras ────────────────────────────────────────────────────────────────
Content-Type: application/json; charset=utf-8
Content-Length: 247                # tamaño del cuerpo en bytes
Date: Wed, 16 Sep 2026 10:30:00 GMT
Server: nginx/1.24                 # software del servidor (a veces se oculta)
Cache-Control: max-age=3600        # instrucciones de caché
ETag: "33a64df5514"                # identificador de la versión del recurso
Set-Cookie: sesion=abc; HttpOnly; Secure  # establecer una cookie

# ─── Línea vacía ──────────────────────────────────────────────────────────────

# ─── Cuerpo ───────────────────────────────────────────────────────────────────
{
  "id": 123,
  "nombre": "Ana García",
  "email": "ana@mail.com"
}

Métodos HTTP

Los métodos HTTP (también llamados verbos) indican qué acción quiere realizar el cliente sobre el recurso.

GET: obtener un recurso

# GET es el método más usado. Obtiene datos sin modificar nada en el servidor.
# Características:
# - Solo lectura: no debe tener efectos secundarios
# - Idempotente: varias llamadas iguales dan el mismo resultado
# - Cacheable: los navegadores y proxies pueden cachear las respuestas
# - Sin cuerpo: los parámetros van en la URL (query string)

GET /productos HTTP/1.1
Host: api.tienda.com
Accept: application/json

# Con parámetros en la URL (query string)
GET /productos?categoria=electronica&precio_max=500&pagina=2 HTTP/1.1

# Respuesta exitosa:
HTTP/1.1 200 OK
Content-Type: application/json

{ "datos": [...], "total": 47 }

 

POST: crear un recurso o enviar datos

# POST envía datos al servidor para crear un recurso o ejecutar una acción.
# Características:
# - No es idempotente: dos llamadas iguales pueden crear dos recursos distintos
# - No es cacheable por defecto
# - Tiene cuerpo: los datos van en el body
POST /usuarios HTTP/1.1
Host: api.tienda.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...
{
 "nombre": "Ana García",
 "email": "ana@mail.com"
}
# Respuesta exitosa: 201 Created
HTTP/1.1 201 Created
Content-Type: application/json
Location: /usuarios/124    # URL del nuevo recurso
{
 "id": 124,
 "nombre": "Ana García",
 "email": "ana@mail.com",
 "creado_en": "2026-09-16T10:30:00Z"
}

PUT y PATCH: actualizar un recurso

# PUT: reemplaza el recurso completo
# Idempotente: varias llamadas con los mismos datos producen el mismo resultado
# Debes enviar TODOS los campos del recurso
PUT /usuarios/124 HTTP/1.1
Content-Type: application/json
{
 "nombre": "Ana García López",
 "email": "ana.garcia@mail.com",
 "edad": 29,
 "activo": true
}
# Si omites un campo, se sobreescribe con null o con el valor por defecto
# PATCH: actualización parcial
# Solo envías los campos que quieres cambiar
# Más eficiente cuando el objeto tiene muchos campos
PATCH /usuarios/124 HTTP/1.1
Content-Type: application/json
{
 "nombre": "Ana García López"
}
# Solo cambia el nombre, el resto de campos no se toca

DELETE: eliminar un recurso

# DELETE elimina el recurso indicado en la URL.
# Idempotente: eliminar algo ya eliminado no debería dar error.
DELETE /usuarios/124 HTTP/1.1
Authorization: Bearer eyJhbGci...
# Respuesta exitosa sin cuerpo:
HTTP/1.1 204 No Content
# Respuesta exitosa con cuerpo (algunas APIs devuelven el recurso eliminado):
HTTP/1.1 200 OK
{ "mensaje": "Usuario 124 eliminado", "id": 124 }

HEAD y OPTIONS

# HEAD: igual que GET pero sin cuerpo en la respuesta
# Útil para comprobar si un recurso existe, obtener sus metadatos
# o verificar la caché sin descargar el contenido completo
HEAD /archivos/video-grande.mp4 HTTP/1.1
# Respuesta: solo las cabeceras (Content-Length, Last-Modified, ETag...)
# sin el cuerpo del archivo
# OPTIONS: obtener los métodos permitidos en un recurso
# El navegador lo usa automáticamente en peticiones CORS (preflight request)
OPTIONS /usuarios HTTP/1.1
Origin: https://mi-app.com
# Respuesta:
HTTP/1.1 204 No Content
Allow: GET, POST, OPTIONS
Access-Control-Allow-Origin: https://mi-app.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

Propiedades de los métodos

MétodoSeguro (solo lectura)IdempotenteCacheableTiene cuerpo
GETSíSíSíNo
HEADSíSíSíNo
OPTIONSSíSíNoNo
POSTNoNoNo*Sí
PUTNoSíNoSí
PATCHNoNo*NoSí
DELETENoSíNoNo*

* Hay excepciones y matices dependiendo de la implementación.

Cabeceras HTTP

Las cabeceras son pares clave-valor que transportan metadatos sobre la petición o la respuesta. Hay cabeceras estándar definidas por el protocolo y cabeceras personalizadas (que por convención empiezan con X-).

Cabeceras de petición más importantes

# Content-Type: formato del cuerpo que envías
Content-Type: application/json           # JSON (APIs REST)
Content-Type: application/x-www-form-urlencoded  # formulario HTML
Content-Type: multipart/form-data        # formulario con archivos
Content-Type: text/plain                 # texto plano
Content-Type: application/xml            # XML
# Accept: qué formato aceptas en la respuesta
Accept: application/json                 # solo JSON
Accept: text/html, application/json      # HTML o JSON
Accept: */*                             # cualquier formato
# Authorization: credenciales de autenticación
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...  # JWT
Authorization: Basic dXNlcjpwYXNz             # usuario:contraseña en base64
Authorization: ApiKey tu-api-key              # API key
# Accept-Encoding: compresión aceptada
Accept-Encoding: gzip, deflate, br      # el servidor puede comprimir la respuesta
# Accept-Language: idioma preferido
Accept-Language: es-ES, es;q=0.9, en;q=0.8
# es-ES: primera opción
# es:    segunda opción (calidad 0.9)
# en:    tercera opción (calidad 0.8)
# User-Agent: identificación del cliente
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
User-Agent: MiApp/2.1 (iOS 17; iPhone 15)
# Host: dominio al que va la petición (obligatorio en HTTP/1.1)
Host: api.ejemplo.com
# Cookie: enviar cookies al servidor
Cookie: sesion_id=abc123; preferencias=oscuro
# Referer: de dónde viene el usuario
Referer: https://google.com/search?q=ejemplo
# If-None-Match: caché condicional con ETag
If-None-Match: "33a64df5514"
# Si el recurso no ha cambiado (mismo ETag), el servidor devuelve 304 Not Modified
# If-Modified-Since: caché condicional por fecha
If-Modified-Since: Wed, 15 Sep 2026 10:00:00 GMT

Cabeceras de respuesta más importantes

# Content-Type: formato del cuerpo de la respuesta
Content-Type: application/json; charset=utf-8
Content-Type: text/html; charset=utf-8
Content-Type: image/jpeg
Content-Type: application/pdf
# Content-Length: tamaño del cuerpo en bytes
Content-Length: 1234
# Content-Encoding: compresión aplicada al cuerpo
Content-Encoding: gzip   # el cliente debe descomprimir
# Location: URL del recurso creado (201) o URL de redirección (301, 302)
Location: /usuarios/124
Location: https://nuevo-dominio.com/ruta
# Set-Cookie: establecer una cookie en el cliente
Set-Cookie: sesion_id=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
# HttpOnly: inaccesible desde JavaScript (protege contra XSS)
# Secure: solo se envía sobre HTTPS
# SameSite=Strict: no se envía en peticiones cross-site (protege contra CSRF)
# Max-Age=3600: expira en 1 hora
# ETag: identificador de la versión del recurso para caché
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d"
# Last-Modified: cuándo se modificó el recurso por última vez
Last-Modified: Wed, 16 Sep 2026 09:00:00 GMT
# Cache-Control: instrucciones de caché
Cache-Control: no-store              # no cachear nunca (datos sensibles)
Cache-Control: no-cache              # cachear pero revalidar siempre
Cache-Control: max-age=3600          # cachear durante 1 hora
Cache-Control: max-age=86400, public # cachear 24h, CDN puede cachearlo también
Cache-Control: private, max-age=300  # cachear 5 min, solo el navegador (no CDN)
# X-RateLimit-*: información sobre el rate limiting
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Reset: 1726484400
# CORS: control de acceso entre orígenes
Access-Control-Allow-Origin: https://mi-app.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400   # cachear el preflight 24h
# Seguridad
Strict-Transport-Security: max-age=31536000; includeSubDomains  # forzar HTTPS
X-Frame-Options: DENY           # no se puede embeber en iframe
X-Content-Type-Options: nosniff # no hacer MIME sniffing

Códigos de estado HTTP

Los códigos de estado indican qué ocurrió con la petición. Son números de tres dígitos agrupados por centenas.

1xx: Informativos

# Raramente los ves en el desarrollo web cotidiano
100 Continue         # el servidor recibió las cabeceras, el cliente puede enviar el cuerpo
101 Switching Protocols  # el servidor acepta cambiar de protocolo (ej: WebSocket)


2xx: Éxito

# La petición se procesó correctamente
200 OK
# Éxito general. El cuerpo contiene el resultado.
# Para: GET, PUT, PATCH exitosos
201 Created
# Se creó un nuevo recurso.
# Incluir cabecera Location con la URL del recurso.
# Para: POST que crea un recurso
202 Accepted
# La petición se aceptó pero el procesamiento es asíncrono (aún no terminó).
# Útil para: tareas largas, colas de procesamiento
# "Tu pedido está siendo procesado"
204 No Content
# Éxito sin cuerpo en la respuesta.
# Para: DELETE exitoso, acciones que no devuelven datos
206 Partial Content
# Respuesta parcial a una petición con cabecera Range.
# Para: descarga de archivos grandes en trozos, streaming de vídeo

3xx: Redirecciones

# El cliente debe hacer otra acción (normalmente ir a otra URL)
301 Moved Permanently
# El recurso se ha movido a una nueva URL de forma permanente.
# Los navegadores y buscadores actualizan su referencia.
# Cabecera Location: nueva URL
302 Found (Previously "Moved Temporarily")
# Redirección temporal. El cliente debe ir a Location
# pero seguir usando la URL original en el futuro.
303 See Other
# Después de un POST exitoso, redirigir a otra URL con GET.
# Patrón POST/Redirect/GET para evitar el reenvío del formulario al recargar.
304 Not Modified
# El recurso no ha cambiado desde la última vez que el cliente lo obtuvo.
# El cliente puede usar su versión en caché.
# No tiene cuerpo.
307 Temporary Redirect
# Como 302 pero garantiza que el método HTTP no cambia.
# (302 permitía cambiar de POST a GET; 307 no)
308 Permanent Redirect
# Como 301 pero garantiza que el método HTTP no cambia.

4xx: Errores del cliente

# El problema está en la petición: datos incorrectos, sin permisos, no encontrado
400 Bad Request
# La petición está mal formada: JSON inválido, campo obligatorio que falta,
# tipo de dato incorrecto, parámetro inválido.
401 Unauthorized  ← nombre confuso: en realidad significa "no autenticado"
# No has proporcionado credenciales o las credenciales son inválidas.
# El servidor devuelve cabecera WWW-Authenticate indicando cómo autenticarse.
403 Forbidden
# Estás autenticado pero no tienes permisos para este recurso.
# El servidor entiende la petición pero se niega a cumplirla.
404 Not Found
# El recurso no existe en esa URL.
# También se usa cuando 403 revelaría que el recurso existe.
405 Method Not Allowed
# El método HTTP no está permitido para ese recurso.
# El servidor devuelve cabecera Allow con los métodos permitidos.
# Ej: POST /usuarios/123 cuando solo existe GET y PATCH
408 Request Timeout
# El servidor esperó demasiado la petición del cliente.
409 Conflict
# Conflicto con el estado actual del recurso.
# Email ya registrado, edición concurrente del mismo recurso.
410 Gone
# El recurso existía pero fue eliminado permanentemente.
# A diferencia de 404, indica que el recurso no volverá.
422 Unprocessable Entity
# La sintaxis es válida pero el contenido semántico no supera la validación.
# Muy usado para errores de validación en APIs REST.
429 Too Many Requests
# Rate limiting: demasiadas peticiones en poco tiempo.
# Cabecera Retry-After indica cuándo puede volver a intentarlo.

5xx: Errores del servidor

# El problema está en el servidor, no en la petición del cliente
500 Internal Server Error
# Error inesperado del servidor.
# El cliente no puede hacer nada para arreglarlo.
# Nunca reveles el stack trace en la respuesta.
501 Not Implemented
# El servidor no implementa el método HTTP solicitado.
502 Bad Gateway
# El servidor actúa como proxy y el servidor upstream dio una respuesta inválida.
# Habitual cuando cae un microservicio.
503 Service Unavailable
# El servidor no puede atender peticiones: sobrecarga o mantenimiento.
# Cabecera Retry-After indica cuándo volver a intentar.
504 Gateway Timeout
# El servidor proxy no recibió respuesta del servidor upstream a tiempo.

HTTP/1.1 vs HTTP/2 vs HTTP/3

# HTTP/1.1 (1997): el estándar durante décadas
# - Una petición por conexión TCP (en la práctica, keep-alive permite reutilizar)
# - Head-of-line blocking: una petición lenta bloquea las demás
# - Las cabeceras se envían sin comprimir en texto plano
# HTTP/2 (2015): mejoras significativas de rendimiento
# - Multiplexing: múltiples peticiones en paralelo sobre una sola conexión TCP
# - Compresión de cabeceras (HPACK): reduce significativamente el overhead
# - Server push: el servidor puede enviar recursos antes de que se pidan
# - Binario en lugar de texto: más eficiente de parsear
# La mayoría de sitios modernos lo usan. Compatible con HTTP/1.1.
# HTTP/3 (2022): basado en QUIC en lugar de TCP
# - QUIC: protocolo de transporte sobre UDP con las garantías de TCP
# - Elimina el head-of-line blocking de TCP (afectaba a HTTP/2)
# - Establecimiento de conexión más rápido (0-RTT en conexiones conocidas)
# - Mejor rendimiento en redes con pérdida de paquetes (WiFi, móvil)
# Verificar qué versión usa un sitio:
# Chrome DevTools → Network → clic en una petición → cabeceras → Protocol
# h2 = HTTP/2, h3 = HTTP/3, http/1.1 = HTTP/1.1

Ver peticiones HTTP en la práctica

# Herramienta 1: Chrome/Firefox DevTools
# F12 → Network → recargar la página
# Clic en cualquier petición para ver:
# - Headers: cabeceras de petición y respuesta
# - Preview/Response: cuerpo de la respuesta
# - Timing: cuánto tardó cada fase (DNS, TCP, TLS, espera, descarga)
# Herramienta 2: curl en la terminal
# Ver una petición GET completa con todas las cabeceras
curl -v https://api.github.com/users/octocat
# > = petición (lo que envía el cliente)
# < = respuesta (lo que devuelve el servidor)
# Hacer una petición POST con JSON
curl -X POST https://api.ejemplo.com/usuarios \
 -H "Content-Type: application/json" \
 -H "Authorization: Bearer mitoken" \
 -d '{"nombre": "Ana", "email": "ana@mail.com"}'
# Ver solo los códigos de estado de una lista de URLs
curl -o /dev/null -s -w "%{http_code}" https://ejemplo.com
# Herramienta 3: httpie (más legible que curl)
# pip install httpie
http GET api.github.com/users/octocat
http POST api.ejemplo.com/usuarios nombre="Ana" email="ana@mail.com"
# Ejemplo práctico: ver el flujo completo con curl verbose
curl -v https://jsonplaceholder.typicode.com/posts/1
# *   Trying 104.21.xxx.xxx:443...
# * Connected to jsonplaceholder.typicode.com
# * TLS handshake...
# > GET /posts/1 HTTP/2                ← método, ruta y versión
# > Host: jsonplaceholder.typicode.com  ← cabeceras enviadas
# > Accept: */*
# >
# < HTTP/2 200                         ← código de estado
# < content-type: application/json     ← cabeceras recibidas
# < cache-control: max-age=43200
# < etag: W/"124-yiKdLzqO5gfBrJFrcdJ8Zw"
# <
# {                                    ← cuerpo de la respuesta
#   "userId": 1,
#   "id": 1,
#   "title": "sunt aut facere...",
#   "body": "quia et suscipit..."
# }

Resumen

  • HTTP es un protocolo sin estado basado en peticiones y respuestas. Cada petición es independiente: el servidor no recuerda el estado de la sesión anterior.
  • Una petición tiene tres partes: línea de petición (método + ruta + versión), cabeceras (metadatos) y cuerpo (datos, solo en POST/PUT/PATCH).
  • GET obtiene datos sin modificar nada (seguro, idempotente, cacheable). POST crea recursos o ejecuta acciones (no idempotente). PUT reemplaza completamente. PATCH actualiza parcialmente. DELETE elimina.
  • Las cabeceras más importantes son: Content-Type (formato del cuerpo), Authorization (credenciales), Accept (formato aceptado), Cache-Control (instrucciones de caché) y Location (URL del recurso creado).
  • Los códigos de estado se agrupan por centenas: 2xx éxito, 3xx redirección, 4xx error del cliente, 5xx error del servidor. Los más habituales: 200, 201, 204, 301, 304, 400, 401, 403, 404, 422, 429, 500.
  • HTTP/2 añade multiplexing y compresión de cabeceras. HTTP/3 usa QUIC en lugar de TCP para mejor rendimiento en redes con pérdida de paquetes.
  • Las DevTools del navegador y curl -v son las herramientas más útiles para ver el tráfico HTTP real y depurar problemas de red.

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a