// Engineering Log

HTTP, HTTPS y TLS: Parte 2 — Cabeceras HTTP: cuáles hay y para qué sirven

Publicado el 28.09.2026

// Ruta rapida

Este articulo pertenece al tema Seguridad y proteccion.

Los encabezados son metadatos de un mensaje HTTP: líneas del tipo Nombre: valor entre la línea de inicio y el cuerpo. A través de ellos el servidor entiende qué sitio se solicita, en qué formato y en qué idioma entregar la respuesta, quién está delante de él y si el cliente tiene una copia actual. El navegador, según los encabezados de respuesta, decide cómo mostrar el contenido, si se puede cachear y qué restricciones de seguridad aplican en la página.

A continuación se analizan los encabezados que aparecen constantemente en el trabajo, por grupos. Puedes verlos en cualquier sitio con el comando:

bash
curl -sI https://example.ru/          # solo cabeceras de la respuesta (método HEAD)
curl -sv -o /dev/null https://example.ru/ 2>&1 | grep -E '^[<>]'   # solicitud y respuesta

Cómo están estructurados los encabezados

  • Los nombres no dependen de mayúsculas/minúsculas. En HTTP/2 y HTTP/3 siempre son minúsculas, y en los registros verás content-type en lugar de Content-Type.
  • Un encabezado puede aparecer varias veces; sus valores se combinan separados por comas. La excepción es Set-Cookie: cada cookie va en una línea separada.
  • El prefijo X- alguna vez indicó encabezados no estándar. RFC 6648 (2012) recomienda dejar de usarlo, pero nombres antiguos como X-Forwarded-For siguen en uso.
  • El tamaño de los encabezados lo limita el servidor. En Nginx el buffer para la línea de petición y los encabezados se define con large_client_header_buffers (por defecto 4 buffers de 8 KB); si las cookies crecieron demasiado, el servidor responderá 400 Request Header Or Cookie Too Large.

Encabezados de la solicitud

Host — nombre del sitio y puerto si no es estándar. En una misma dirección IP pueden alojarse cientos de sitios, y el servidor web selecciona el correcto precisamente por Host (en Nginx — por server_name). Este es el único encabezado obligatorio en HTTP/1.1. En HTTP/2 y HTTP/3 su papel lo desempeña el pseudo-encabezado :authority.

User-Agent — quién hace la solicitud: el navegador y su versión, curl/8.7.1, python-requests/2.32. Los servidores lo usan para estadísticas y a veces para bloquear bots. El valor lo rellena el propio cliente, por lo que cualquiera puede falsificarlo; indicios más fiables del cliente son las huellas TLS, sobre ellas — «Qué son JA3 y JA4».

Accept, Accept-Language, Accept-Encoding — negociación de contenido. El cliente enumera lo que puede aceptar, con pesos q:

Accept: text/html,application/xhtml+xml,*/*;q=0.8
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br, zstd

El servidor elige una variante y comunica la elección en Content-Type, Content-Language y Content-Encoding. Si la respuesta depende de uno de estos encabezados, el servidor debe añadir Vary: Accept-Encoding (u otro nombre), de lo contrario los proxy y CDN pueden dar una versión comprimida a un cliente que no la entiende.

Authorization — credenciales. Basic — usuario y contraseña en Base64 (es una codificación, no un cifrado: sin HTTPS la contraseña se lee tal cual). Bearer — token, por ejemplo JWT o clave API.

Cookie — cookies que el navegador guardó para este sitio: Cookie: session=abc123; lang=ru.

Referer — dirección de la página desde la que vino el usuario (la palabra está mal escrita ya en la primera especificación y así quedó). Cuánta información enviar se determina por el encabezado de respuesta Referrer-Policy; por defecto los navegadores envían a otros sitios solo el dominio.

Origin — esquema, host y puerto de la página desde la que se envió la solicitud. El navegador lo añade a solicitudes a otro sitio y a POST; sobre él se basan CORS y la comprobación contra CSRF.

If-None-Match, If-Modified-Since — solicitudes condicionales. El navegador informa qué versión ya tiene y el servidor responde con un corto 304 Not Modified sin cuerpo si no ha cambiado nada.

Range — petición de una parte del archivo: Range: bytes=1000000-. Sirve para reanudar descargas y para seek en vídeo.

Content-Type, Content-Length — tipo y longitud del cuerpo de la solicitud. Para formularios — application/x-www-form-urlencoded o multipart/form-data, para APIs — normalmente application/json. Error frecuente: JSON enviado sin Content-Type: application/json, y la aplicación no ve los datos.

Encabezados de respuesta sobre el contenido

Content-Type — qué hay en el cuerpo y en qué codificación: text/html; charset=utf-8, application/json, image/webp. El navegador decide cómo mostrar la respuesta basándose en él. Para evitar que el navegador intente adivinar el tipo (lo que llevaba a ejecutar archivos subidos por usuarios como scripts) se añade X-Content-Type-Options: nosniff.

Content-Encoding — cómo está comprimido el cuerpo: gzip, br (Brotli), zstd.

Content-Disposition — mostrar el archivo en el navegador (inline) o sugerir descargarlo (attachment; filename="report.pdf").

Location — dirección para redirección en respuestas 3xx y dirección del recurso creado en la respuesta 201 Created.

Server — programa del servidor web. Es mejor no revelar la versión: en Nginx — server_tokens off;.

Caché

Hay caché en el navegador, en CDN y en proxies. Las reglas para ellos las define el servidor.

Cache-Control — el encabezado principal de caché:

  • max-age=3600 — la respuesta es fresca durante una hora; en ese tiempo el navegador la toma de la caché sin consultar al servidor;
  • s-maxage=600 — lo mismo para cachés compartidas (CDN, proxies), tiene prioridad sobre max-age;
  • no-cache — se puede almacenar, pero antes de usar se debe verificar con el servidor (vía If-None-Match);
  • no-store — no almacenar en absoluto; para páginas con datos personales;
  • private — solo el navegador, las CDN no deben almacenarla; para páginas que dependen del usuario;
  • public — se puede almacenar en cachés compartidas;
  • immutable — el recurso nunca cambiará; se usa en archivos con hash en el nombre (app.3f9a1c.js) junto con un max-age grande.

El nombre no-cache induce a confusión: no prohíbe el almacenamiento en caché. Para prohibirlo hay no-store.

ETag — identificador de la versión del recurso (por ejemplo, hash del contenido). El navegador lo devuelve en If-None-Match. Last-Modified — fecha de modificación, se devuelve y se usa en If-Modified-Since.

Age — cuántos segundos lleva la respuesta en la caché del CDN o proxy. Si hay Age, la respuesta no vino de tu servidor. Los CDN con frecuencia añaden encabezados propios: CF-Cache-Status: HIT en Cloudflare, X-Cache: HIT en muchos otros.

Cookies

El servidor establece cookies con el encabezado Set-Cookie, una por cada cookie:

Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400
  • Secure — enviar solo por HTTPS;
  • HttpOnly — no accesible desde JavaScript, un script inyectado no la robará;
  • SameSite=Lax — no enviar en solicitudes desde sitios externos, salvo la navegación normal por enlace; Strict — nunca desde sitios externos; None — siempre (solo junto con Secure). Los navegadores modernos, si no se especifica, tratan las cookies como Lax;
  • Max-Age o Expires — tiempo de vida; sin ellos la cookie se borra al cerrar el navegador;
  • Domain — si se especifica, la cookie se envía también a subdominios. Sin él — solo al host que la estableció.

Para una cookie de sesión, el mínimo sensato es Secure; HttpOnly; SameSite=Lax.

CORS

El navegador no permite que JavaScript de la página https://app.example.ru lea respuestas de https://api.example.ru si el servidor API no lo ha permitido explícitamente. Esta regla es la política de mismo origen (same-origin policy), y CORS es el mecanismo de permisos:

Access-Control-Allow-Origin: https://app.example.ru
Access-Control-Allow-Credentials: true

Para solicitudes “no simples” — con métodos PUT y DELETE, con Content-Type: application/json, con el encabezado Authorization — el navegador primero envía una petición preliminar OPTIONS con los encabezados Access-Control-Request-Method y Access-Control-Request-Headers. El servidor responde qué está permitido (Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Max-Age), y solo entonces se envía la solicitud real.

Detalles importantes:

  • CORS protege al usuario del navegador, no al servidor. curl y cualquier script fuera del navegador no aplican CORS;
  • Access-Control-Allow-Origin: * no funciona junto con cookies y Authorization — para peticiones con credenciales se necesita un origen concreto;
  • si el error CORS se ve en la consola, primero mira la respuesta al OPTIONS: a menudo la bloquea la autenticación o el servidor responde con 404/405.

Encabezados de seguridad

  • Strict-Transport-Security (HSTS) — “accede a este sitio solo por HTTPS”: max-age=31536000; includeSubDomains. Más detalles — «Transición a HTTPS: redirecciones, HSTS, HTTPS-First».
  • Content-Security-Policy — de dónde la página puede cargar scripts, estilos, imágenes, a dónde enviar formularios. La principal protección contra XSS. Ejemplo: default-src 'self'; img-src 'self' data:; frame-ancestors 'none'. Es cómodo empezar con Content-Security-Policy-Report-Only: el navegador informa de las violaciones en la consola, pero no bloquea nada.
  • X-Frame-Options: DENY o la directiva CSP frame-ancestors — prohíben incrustar el sitio en un iframe en páginas ajenas (protección contra clickjacking).
  • X-Content-Type-Options: nosniff — no adivinar el tipo de contenido.
  • Referrer-Policy: strict-origin-when-cross-origin — no enviar a sitios externos la URL completa de la página.
  • Permissions-Policy — qué capacidades del navegador están disponibles para la página: camera=(), microphone=(), geolocation=().

El encabezado X-XSS-Protection está obsoleto: los navegadores modernos lo ignoran; en su lugar se usa CSP.

En Nginx los encabezados de respuesta se añaden con la directiva add_header, y tiene una trampa: si en un bloque location hay al menos un add_header, los encabezados del nivel server no se aplican en ese bloque. El parámetro always es necesario para que el encabezado se envíe también en respuestas de error:

nginx
server {
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}

Encabezados de proxy

Cuando delante de la aplicación hay Nginx, HAProxy o un CDN, la aplicación ve la conexión desde el proxy, no desde el cliente. El proxy transmite los datos originales en encabezados:

  • X-Forwarded-For: 192.0.2.7, 192.0.2.2 — cadena de direcciones: el cliente y todos los proxies en la ruta;
  • X-Forwarded-Proto: https — por qué protocolo vino el cliente; sin él, la aplicación detrás de un proxy que recibe HTTPS pensará que funciona por HTTP y construirá enlaces incorrectos o caerá en un redireccionamiento infinito a HTTPS;
  • X-Forwarded-Host — Host original;
  • X-Real-IP — dirección del cliente en un solo valor (convención de Nginx);
  • Forwarded: for=192.0.2.7;proto=https — sustitución estándar para todos los anteriores (RFC 7239), menos soportada.

Se puede confiar en estos encabezados solo si los puso tu proxy. El cliente puede enviar X-Forwarded-For por su cuenta, y una aplicación que tome la primera dirección de la lista recibirá una falsificación. Regla: toma la dirección añadida por el último proxy de confianza, y en Nginx especifica explícitamente las redes de confianza:

nginx
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

Encabezados de control de la conexión

Connection, Keep-Alive, Transfer-Encoding, Upgrade se refieren a un único tramo del recorrido — entre el cliente y el proxy más cercano — y no se transmiten más allá. En HTTP/2 y HTTP/3 están prohibidos. Por eso, para proxificar WebSocket a través de Nginx, hay que reenviar Upgrade y Connection explícitamente a la aplicación — ejemplo en el artículo «Transición a HTTPS y el encabezado Upgrade».

Errores comunes

  • No se definió Cache-Control — los navegadores y CDN hacen caché por heurística (a menudo 10 % de la antigüedad de Last-Modified), y los usuarios ven estilos antiguos después del despliegue.
  • no-cache en lugar de no-store en páginas con datos personales.
  • La respuesta depende de cookies o del idioma, pero no se puso Vary ni private — el CDN da a un usuario la página de otro.
  • Access-Control-Allow-Origin se toma del Origin de la solicitud sin verificarlo en una lista — es lo mismo que permitir a todos, pero con cookies.
  • La aplicación confía en X-Forwarded-For de cualquier cliente — se elude la restricción por IP y los registros se falsifican.

// Tarea parecida

Si estas resolviendo algo parecido

Este articulo pertenece a uno de los temas principales de trabajo. Puedes seguir leyendo sobre el tema, ir a la pagina principal para entender a que me dedico o abrir directamente los servicios.

Tema del articulo

Seguridad y proteccion

SSL, hardening, accesos, proteccion de servicios y configuraciones seguras.

Tareas frecuentes de esta tema

  • Configurar SSL, certificados y conexiones seguras
  • Restringir accesos y cerrar puntos de entrada innecesarios
  • Reforzar la configuracion del servidor y los servicios

// Siguiente paso

Si necesitas ayuda con este tema y no solo otro articulo, es mejor ir directo a la pagina del servicio. La pagina principal y la seleccion de materiales quedan como rutas secundarias.

Abrir servicios

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Respondo en un día laborable (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirme que no es un bot.

Escribir y recibir una respuesta rápida