Por qué una empresa de proxy escribe sobre los códigos de estado: somos Geonode y la gente canaliza el tráfico de la API a través de nosotros, por lo que nos preguntan si un error concreto se debe al «proxy». En el caso del 415, la respuesta es, básicamente, siempre «no». Un 415 proviene del servidor de origen y describe algún aspecto de la solicitud que has creado. Un intermediario puede generar uno en circunstancias muy concretas —un proxy de filtrado que inspecciona los cuerpos de las solicitudes, una pasarela con sus propias reglas de contenido—, pero eso es poco habitual y se indica en los encabezados de la respuesta. Si recibes un 415 a través de un proxy, elimina el proxy y es casi seguro que obtendrás el mismo 415 directamente. Corrige la solicitud. El único código de estado que realmente implica la presencia de un proxy es el 407, y así lo indica su nombre.
Ahora, el error en sí.
Lo que dice la especificación
RFC 9110, en la sección 15.5.16, lo define con precisión:
El código de estado 415 (Tipo de medio no admitido) indica que el servidor de origen se niega a atender la solicitud porque el contenido tiene un formato que no es compatible con este método en el recurso de destino.
Tres partes de esa frase sí que funcionan.
«El contenido»: el cuerpo de la solicitud, no la URL, ni la cadena de consulta, ni la respuesta. Si tu solicitud no tiene cuerpo, un código 415 es inusual y sugiere que está ocurriendo algo más.
«No es compatible con este método»: la compatibilidad se establece por método. Un recurso puede aceptar application/json en POST y rechazarlo en PATCH, lo cual es una fuente de confusión realmente habitual cuando un mismo punto final se comporta de forma diferente según los verbos utilizados.
«En el recurso de destino» — y por recurso. Que un punto final de una API acepte un formato no dice nada sobre otro.
A continuación, la especificación enumera las causas:
El problema de formato podría deberse al Content-Type o al Content-Encoding indicados en la solicitud, o ser el resultado de inspeccionar los datos directamente.
Esa última cláusula es importante y suele pasarse por alto. Un servidor puede devolver un 415 tras examinar los bytes, no solo tras leer los encabezados. Declarar «Content-Type: application/json» y enviar algo que no sea JSON puede dar lugar legítimamente a un 415 en lugar de a un 400.
El RFC también especifica qué debe indicarte el servidor. Si el problema fuera la codificación del contenido, establece que el encabezado de respuesta «Accept-Encoding» «debe utilizarse para indicar qué codificaciones de contenido (si las hubiera) se habrían aceptado». Si se tratara del tipo de medio, «Accept» «puede utilizarse para indicar qué tipos de medio se habrían aceptado». En la práctica, los documentos de MDN indican que los servidores suelen utilizar Accept-Post y Accept-Patch para los casos específicos de cada método, lo cual resulta aún más útil.
Lee los encabezados de respuesta. Los servidores suelen darte la respuesta, pero los clientes suelen ignorarla.
Las seis causas
Por orden aproximado de frecuencia con la que las vemos.
1. No incluir «Content-Type» en absoluto. Envías un cuerpo y nunca declaras su formato. Muchos marcos de trabajo no lo adivinarán. El ejemplo de MDN es exactamente este: un POST con un cuerpo JSON, un «Content-Length» y sin «Content-Type», al que se responde con «415» y «Accept-Post: application/json; charset=UTF-8».
2. El tipo de medio «Content-Type» incorrecto. El caso clásico es enviar JSON mientras se declara «application/x-www-form-urlencoded», normalmente porque un cliente HTTP utiliza por defecto la codificación de formulario y se ha pasado una cadena JSON sin modificarla. El cuerpo es correcto; la etiqueta es errónea.
3. Un tipo de medio «casi correcto, pero erróneo». «text/json» en lugar de «application/json». «application/xml» cuando el servidor espera «text/xml». Tipos de proveedor como application/vnd.api+json cuando has enviado application/json sin formato. Los servidores estrictos realizan una coincidencia exacta y no serán indulgentes.
4. Problemas con el juego de caracteres. MDN ofrece el ejemplo más claro: enviar UTF8 cuando el servidor requiere UTF-8. El guión no es opcional en el nombre registrado, y un servidor que realice una validación estricta de los parámetros está en su derecho de rechazarlo.
5. Content-Encoding que el servidor no admite. Comprimís el cuerpo de la solicitud con gzip y establecéis Content-Encoding: gzip en un servidor que solo admite la codificación de identidad. El RFC prevé este caso específicamente, y un servidor que funcione correctamente debería devolver Accept-Encoding indicándoos qué es lo que necesita.
6. El cuerpo no coincide con el tipo declarado. Encabezado correcto, bytes incorrectos: a menudo se trata de un error de serialización, de una plantilla que ha generado una cadena vacía o de un cuerpo que ha sido codificado dos veces en algún punto de la pila. Aquí es donde entra en juego la cláusula de «inspección directa de los datos».
415 frente a 406 frente a 400 frente a 422
Este es el mapa de confusiones, y la especificación los distingue claramente.
| Código | Qué significa | Dirección | Solución mediante cambio |
|---|
| 415 | El formato enviado no es compatible | Cuerpo de la solicitud | Content-Type o Content-Encoding |
| 406 | No hay ninguna representación aceptable disponible | Respuesta | Encabezado «Accept» |
| 400 | La solicitud tiene un formato incorrecto | Solicitud completa | Sintaxis o estructura |
| 422 | Formato comprendido, contenido no procesable | Cuerpo de la solicitud | Los propios datos |
| 413 | Cuerpo demasiado grande | Cuerpo de la solicitud | Tamaño de la carga útil |
La diferencia entre 415 y 406 es una cuestión de orientación y, una vez explicada, es la más fácil de entender. El 415 se refiere a lo que has enviado. El 406 se refiere a lo que has solicitado recibir: el RFC 9110 lo define como que el recurso no tiene «una representación actual que sea aceptable para el agente de usuario, según los campos de encabezado de negociación proactiva recibidos». Si recibes un 406, fíjate en tu encabezado «Accept», no en el cuerpo.
415 frente a 400. El RFC 9110 describe el 400 como el caso en el que el servidor no procesa la solicitud «debido a algo que se percibe como un error del cliente (por ejemplo, sintaxis de la solicitud mal formada, estructura del mensaje de solicitud no válida o enrutamiento engañoso de la solicitud)». El código 400 es estructural: la propia solicitud está defectuosa. El 415 corresponde a una solicitud bien formada cuyo cuerpo tiene un formato que el servidor no acepta. En la práctica, muchos servidores devuelven un 400 cuando un 415 sería más preciso; no puedes controlar esto, así que trata un 400 en una solicitud con cuerpo como si fuera, posiblemente, un 415 encubierto.
415 frente a 422 es la distinción que el RFC establece de forma más explícita. La sección 15.5.21 establece que el 422 indica que «el servidor comprende el tipo de contenido de la solicitud (por lo que un código de estado 415 [Tipo de medio no admitido] es inadecuado) y que la sintaxis del contenido de la solicitud es correcta, pero no ha podido procesar las instrucciones que contiene».
Así pues, la jerarquía es la siguiente: 415 significa que el contenedor es incorrecto; 422 significa que el contenedor es correcto y el contenido es incorrecto. Un JSON bien formado al que le falte un campo obligatorio es un 422. El mismo JSON etiquetado como datos de formulario es un 415.
Cómo diagnosticarlo en menos de dos minutos
Una secuencia fija que resuelve casi todos los casos.
Paso uno: lee los encabezados de la respuesta. No la línea de estado, sino los encabezados.
curl -i -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
Busca Accept, Accept-Post, Accept-Patch o Accept-Encoding en la respuesta. Si aparece alguno de ellos, es una indicación directa de lo que quiere el servidor y ya está.
Paso dos: confirma lo que realmente enviaste. No lo que pretendías enviar, sino lo que se transmitió por la red. Las bibliotecas de cliente añaden, sobrescriben y reformatean los encabezados, y el encabezado que estableces en el código no siempre es el que se ha transmitido.
curl -v -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}' 2>&1 | grep '^>'
Las líneas «>» son tu solicitud real. Una proporción sorprendente de los errores 415 se resuelve precisamente aquí, cuando resulta que el «Content-Type» que has configurado cuidadosamente ha sido sobrescrito por un valor predeterminado.
Paso tres: comprueba el método. Un mismo punto final puede aceptar un tipo con el método POST y rechazarlo con el método PATCH. Prueba el mismo cuerpo con un verbo diferente y comprueba si el comportamiento cambia.
Paso cuatro: comprueba la cadena exacta del tipo. Compárala carácter por carácter con la documentación. application/json frente a text/json. UTF-8 frente a UTF8. Sufijos de los proveedores. Esto es tedioso, pero suele ser ahí donde se encuentra la respuesta.
Paso cinco: lee la documentación de ese punto final específico. Las API no son uniformes internamente. Es totalmente normal que un punto final de subida de archivos requiera multipart/form-data en una API que, por lo demás, es JSON.
Solución en el lado del cliente
Los casos más habituales en los clientes más comunes.
curl. -d implica application/x-www-form-urlencoded, a menos que se indique lo contrario. Esta es la causa más habitual de un error 415 desde la línea de comandos:
curl -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
fetch de JavaScript. Al pasar un cuerpo de cadena, no se establece ningún Content-Type:
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "test" }),
});
La excepción que conviene conocer: con FormData, no establezcas tú mismo Content-Type. El navegador debe generarlo porque incluye el límite multipart, y sobrescribirlo produce una solicitud errónea que a menudo se manifiesta como un error 415.
Python requests. Utiliza json= en lugar de data= y el encabezado se establecerá automáticamente:
requests.post(url, json={"name": "test"}) # application/json
requests.post(url, data={"name": "test"}) # form-encoded
Axios. Establece application/json para objetos simples y algo distinto para cadenas, lo cual suele ser motivo de sorpresa. Si ya has serializado tu carga útil, configura el encabezado de forma explícita. Las diferencias de comportamiento entre los clientes en este caso son exactamente el tipo de cosas que tratamos en axios vs fetch.
Una regla general: cuando un cliente ofrezca un parámetro específico para JSON, utilízalo en lugar de serializar manualmente y confiar en que el encabezado predeterminado sea el correcto.
Devolverlo correctamente en el lado del servidor
Si te encuentras en el otro extremo, hay algunos aspectos que facilitan considerablemente el trabajo con tu API.
Envía un encabezado «Accept-Post» o «Accept-Patch» junto con el código 415. El RFC exige «Accept» o «Accept-Encoding»; las variantes específicas de cada método son más precisas y MDN las documenta precisamente para este uso. Este único encabezado convierte una sesión de depuración en un resumen rápido.
Incluye un cuerpo legible. Un código de estado con un cuerpo vacío obliga al cliente a adivinar. Indica lo que has recibido y lo que esperabas.
Distingue correctamente entre el 415 y el 422. Si has entendido el tipo de contenido y has analizado el cuerpo, pero ha fallado la validación, se trata de un 422. Devolver un 415 por fallos de validación hace que los usuarios comprueben sus encabezados cuando estos están correctos, y es un error común y costoso en el diseño de API.
Sé flexible con los parámetros siempre que puedas hacerlo de forma segura. Rechazar application/json; charset=utf-8 cuando aceptas application/json es técnicamente defendible, pero en la práctica no sirve de nada. Analiza el tipo de medio correctamente e ignora los parámetros que no te interesen.
No utilices el código 415 como rechazo genérico. Tiene un significado específico. Sobrecargarlo dificulta el uso de tu API y hace que la lógica de reintentos del lado del cliente sea errónea.
Cuando un 415 no es realmente un 415
Casos en los que el código de estado induce a error.
Una pasarela o un WAF ha rechazado la solicitud. Algunas capas de seguridad devuelven un 415 para los cuerpos que consideran sospechosos, independientemente del tipo de contenido real. La pista suele ser un cuerpo de respuesta que no parece proceder de la aplicación, o encabezados que identifican a un intermediario.
Se ha activado una configuración predeterminada del marco de trabajo antes de que se ejecutara tu código. Muchos marcos de trabajo web rechazan tipos de contenido desconocidos en el middleware. Tu controlador nunca se ejecutó, por lo que nada de la lógica de tu aplicación es relevante para la solución.
Un equilibrador de carga o una CDN ha eliminado un encabezado. Es poco frecuente, pero ocurre. Si la solicitud funciona cuando se envía directamente y falla al pasar por la infraestructura, compara los encabezados en ambos extremos antes de dar por hecho que la aplicación ha cambiado.
El punto final no existe. Algunos servidores responden a una ruta no coincidente en un POST con un código de estado 415 en lugar de 404, ya que la negociación del tipo de contenido se produce antes de que se resuelva el enrutamiento. Comprueba la URL.
La anulación del método HTTP ha fallado. Si tu marco de trabajo admite anular el método mediante un encabezado o un parámetro de consulta, es posible que el método efectivo no sea el que enviaste, y la compatibilidad con los tipos de contenido depende de cada método.
En todos estos casos, la solución se encuentra antes de tu carga útil. El principio general es: si la solicitud es obviamente correcta y el error 415 persiste, deja de editar el cuerpo y empieza a averiguar qué componente de la ruta está generando la respuesta.
Preguntas frecuentes
¿Qué significa el error 415 «Unsupported Media Type»?
El servidor ha rechazado la solicitud porque el formato del cuerpo de la misma no es compatible con ese método en ese recurso. La norma RFC 9110 lo atribuye al encabezado «Content-Type», al encabezado «Content-Encoding» o a que el servidor inspecciona el cuerpo directamente. Se trata del formato de lo que has enviado, no de la corrección de los datos.
¿Cómo soluciono un error 415?
Comprueba primero los encabezados de respuesta: los servidores suelen devolver Accept, Accept-Post o Accept-Encoding indicando exactamente lo que quieren. A continuación, verifica lo que tu cliente ha transmitido realmente, ya que las bibliotecas anulan los encabezados. La solución más habitual es añadir Content-Type: application/json a una solicitud que no lo tuviera.
¿Cuál es la diferencia entre 415 y 400?
El código 400 significa que la solicitud está mal formada: sintaxis o estructura incorrectas. El código 415 significa que la solicitud está bien formada, pero el cuerpo tiene un formato no compatible. En la práctica, los servidores suelen devolver un 400 cuando un 415 sería más preciso, por lo que merece la pena investigar un 400 en una solicitud con cuerpo como un posible problema de tipo de contenido.
¿Cuál es la diferencia entre 415 y 422?
La RFC 9110 establece esta distinción de forma explícita: el código 422 significa que el servidor entendió el tipo de contenido y que la sintaxis era correcta, pero no pudo procesar las instrucciones. Así pues, el 415 se refiere a que el contenedor es incorrecto; el 422, a que el contenido es incorrecto. Un JSON válido al que le falte un campo obligatorio da lugar a un 422.
¿Por qué me aparece un 415 al subir archivos?
Normalmente se debe a que se ha establecido manualmente Content-Type en una solicitud multiparte. El navegador o el cliente debe generar ese encabezado por sí mismo, ya que contiene el límite multiparte. Si lo estableces tú mismo, se elimina el límite y se genera una solicitud que el servidor no puede analizar.
¿Puede un proxy provocar un 415?
Rara vez. El error 415 proviene del servidor de origen y se refiere al cuerpo de tu solicitud. Un proxy de filtrado o una pasarela que inspeccione el contenido puede generarlo, pero el código de estado específico de los proxies suele ser el 407, que lo indica en su propio nombre. Prueba sin el proxy: si el error 415 persiste, el proxy no ha tenido nada que ver.
¿Significa el error 415 que mi JSON no es válido?
No necesariamente. Si el servidor rechazó el tipo de contenido, tu JSON ni siquiera se examinó. Si el servidor declaró el tipo correcto y luego detectó que el cuerpo no tenía realmente ese formato, entonces sí: el RFC permite el rechazo tras «inspeccionar los datos directamente». Comprueba primero si se trata de un problema de encabezado; es mucho más habitual.
¿Debo volver a intentarlo tras un 415?
No. Se trata de un error del cliente y la misma solicitud producirá el mismo resultado. Volver a intentarlo supone un desperdicio de solicitudes y, si tienes una limitación de frecuencia, puede empeorar las cosas. Corrige el tipo de contenido y envíalo una sola vez.
Conclusión
El código 415 tiene un significado concreto y preciso: el contenedor que envuelve tus datos no es uno de los que este punto final acepta para este método. No se trata de que tus datos sean inválidos —para eso está el código 422— ni de lo que has solicitado recibir —para eso está el código 406—.
Dado que el significado es concreto, el diagnóstico es breve. Lee los encabezados de la respuesta, ya que un servidor que funcione correctamente indica los tipos aceptables en Accept, Accept-Post o Accept-Encoding. A continuación, comprueba lo que tu cliente ha enviado realmente por la red, en lugar de lo que le indicaste, ya que los valores por defecto y el middleware suelen anular el encabezado que tú has establecido. Con estos dos pasos resolverás la mayoría de los casos sin tener que tocar el cuerpo de la solicitud en absoluto.
Y si la solicitud parece impecable y el error 415 persiste, es probable que la respuesta no provenga de la aplicación que crees. Las pasarelas, el middleware de los marcos de trabajo y las rutas no coincidentes producen errores 415 que no tienen nada que ver con tu carga útil; en ese momento, la pregunta útil no es qué cambiar, sino qué componente de la ruta está respondiendo.