Cabecera Content-Type

Fundamentos Jan 9, 2026 HTTP
http headers content-negotiation mime media-types

Definición

La cabecera Content-Type es una cabecera HTTP que especifica el tipo de medio (tipo MIME) del cuerpo de petición o respuesta. Dice al receptor cómo interpretar los datos que se están enviando.

La cabecera consiste en:

  1. Tipo/Subtipo - Tipo de medio (ej. application/json, text/html)
  2. Parámetros Opcionales - Codificación de caracteres, límites (ej. charset=utf-8)

Valores comunes de Content-Type:

  • application/json - Datos JSON
  • application/xml - Datos XML
  • text/html - Documentos HTML
  • text/plain - Texto plano
  • multipart/form-data - Subidas de archivos
  • application/x-www-form-urlencoded - Envíos de formularios

Content-Type es crítico para APIs REST, ya que determina cómo el servidor parsea peticiones entrantes y cómo los clientes interpretan respuestas.

Ejemplo

Petición JSON:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 89

{
  "name": "Alice Smith",
  "email": "[email protected]",
  "role": "developer"
}

Respuesta JSON:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 142

{
  "id": 123,
  "name": "Alice Smith",
  "email": "[email protected]",
  "createdAt": "2026-01-09T10:30:00Z"
}

Subida de Archivo (multipart/form-data):

POST /api/upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="document.pdf"
Content-Type: application/pdf

[Datos binarios PDF]
------WebKitFormBoundary7MA4YWxkTrZu0gW--

Ejemplo de Código

JavaScript (Fetch API):

// Enviar JSON (establecimiento automático de Content-Type)
const createUser = async (userData) => {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json', // Establecer explícitamente
      'Authorization': 'Bearer YOUR_TOKEN'
    },
    body: JSON.stringify(userData) // Debe coincidir con Content-Type
  });

  // Verificar Content-Type de respuesta
  const contentType = response.headers.get('Content-Type');
  console.log('Response Content-Type:', contentType);

  if (contentType && contentType.includes('application/json')) {
    return await response.json();
  } else if (contentType && contentType.includes('text/plain')) {
    return await response.text();
  } else {
    throw new Error(`Unexpected Content-Type: ${contentType}`);
  }
};

// Enviar Form Data (Content-Type automático con boundary)
const uploadFile = async (file) => {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('description', 'User uploaded file');

  const response = await fetch('https://api.example.com/upload', {
    method: 'POST',
    // NO establecer Content-Type manualmente para FormData
    // El navegador lo establece con el boundary correcto
    body: formData
  });

  return await response.json();
};

// Enviar Datos URL-encoded de Formulario
const submitForm = async (formData) => {
  const params = new URLSearchParams(formData);

  const response = await fetch('https://api.example.com/form', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: params.toString()
  });

  return await response.json();
};

Python (librería requests):

import requests

# Enviar JSON
def create_user(user_data):
    response = requests.post(
        'https://api.example.com/users',
        json=user_data,  # Establece automáticamente Content-Type: application/json
        headers={'Authorization': 'Bearer YOUR_TOKEN'}
    )

    # Verificar Content-Type de respuesta
    content_type = response.headers.get('Content-Type')
    print(f'Response Content-Type: {content_type}')

    if content_type and 'application/json' in content_type:
        return response.json()
    elif content_type and 'text/plain' in content_type:
        return response.text
    else:
        raise ValueError(f'Unexpected Content-Type: {content_type}')

# Enviar Subida de Archivo
def upload_file(file_path):
    with open(file_path, 'rb') as file:
        files = {'file': ('document.pdf', file, 'application/pdf')}
        data = {'description': 'User uploaded file'}

        response = requests.post(
            'https://api.example.com/upload',
            files=files,
            data=data
            # Content-Type se establece automáticamente con boundary
        )

    return response.json()

# Enviar Datos URL-encoded de Formulario
def submit_form(form_data):
    response = requests.post(
        'https://api.example.com/form',
        data=form_data,  # Establece automáticamente Content-Type: application/x-www-form-urlencoded
        headers={'Authorization': 'Bearer YOUR_TOKEN'}
    )

    return response.json()

Diagrama

graph TB
    subgraph "Content-Types de Petición"
        REQ1[application/json]
        REQ2[application/xml]
        REQ3[multipart/form-data]
        REQ4[application/x-www-form-urlencoded]
        REQ5[text/plain]
    end

    subgraph "Content-Types de Respuesta"
        RES1[application/json]
        RES2[text/html]
        RES3[application/pdf]
        RES4[image/png]
        RES5[text/csv]
    end

    subgraph "Procesamiento del Servidor"
        PARSE[Parsear Cuerpo Basado en Content-Type]
        VALIDATE[Validar Formato]
        PROCESS[Procesar Datos]
    end

    REQ1 --> PARSE
    REQ2 --> PARSE
    REQ3 --> PARSE
    REQ4 --> PARSE
    REQ5 --> PARSE

    PARSE --> VALIDATE
    VALIDATE --> PROCESS

    PROCESS --> RES1
    PROCESS --> RES2
    PROCESS --> RES3
    PROCESS --> RES4
    PROCESS --> RES5

Analogía

Piensa en Content-Type como una etiqueta en el empaquetado de alimentos:

  • Content-Type: application/json → “Esto es JSON, parsearlo como datos estructurados”
  • Content-Type: text/plain → “Esto es texto plano, no se necesita parseo especial”
  • Content-Type: multipart/form-data → “Esto contiene múltiples partes (archivos + campos)”

Sin la etiqueta, no sabrías si es sopa, cereal o pizza congelada. Sin Content-Type, el servidor no sabe cómo interpretar los datos.

Mejores Prácticas

  1. Siempre Establecer Content-Type - Especificar el formato del cuerpo en peticiones y respuestas
  2. Coincidir Formato del Cuerpo - Asegurar que el contenido real coincide con el Content-Type declarado
  3. Incluir Charset - Añadir charset=utf-8 para formatos basados en texto
  4. Validar en Servidor - Verificar Content-Type antes de parsear el cuerpo
  5. Usar Tipos MIME Estándar - Seguir tipos de medio registrados por IANA
  6. No Sobrescribir FormData - Dejar que el navegador establezca Content-Type con boundary para subidas multipart
  7. Establecer Tipo Correcto para Descargas - Usar application/octet-stream o tipos específicos para descargas de archivos

Errores Comunes

  • Content-Type Faltante - Enviar JSON sin Content-Type: application/json
  • Content-Type Incorrecto - Declarar application/json pero enviar XML o texto plano
  • Boundary Hardcodeado - Establecer manualmente boundary en multipart/form-data en lugar de dejar que el cliente lo genere
  • Sin Charset - No especificar charset=utf-8 para respuestas de texto/JSON
  • Ignorar Content-Type - Parsear cuerpo de petición sin verificar Content-Type primero
  • Usar application/json para HTML - Devolver HTML con Content-Type: application/json
  • Confiar en Cabecera del Cliente - No validar que Content-Type coincide con el contenido real

Estándares & RFCs

Términos Relacionados