HTTP 415 Unsupported Media Type

Cos'e HTTP 415 Unsupported Media Type

Il codice HTTP 415 Unsupported Media Type indica che il server rifiuta di processare la richiesta perché il Content-Type indicato dal client non e supportato da quell'endpoint, o non corrisponde al formato effettivamente atteso. E un'errore comune nelle API REST quando il client invia dati nel formato sbagliato.

Cause tipiche

  • Client invia application/xml a un'endpoint che accetta solo application/json.
  • Header Content-Type mancante o malformato.
  • Charset incompatibile (es. application/json; charset=latin-1 su API UTF-8 only).
  • Upload di file con MIME type non in whitelist (es. .exe quando si accettano solo immagini).
  • Encoding del body non supportato (es. Content-Encoding: br su server senza brotli).

Esempio classico

POST /api/users HTTP/1.1
Content-Type: application/xml

<user><name>Mario</name></user>

HTTP/1.1 415 Unsupported Media Type
Content-Type: application/json
Accept: application/json

{
  "error": "Only application/json is accepted on this endpoint",
  "supported_types": ["application/json"]
}

Header Accept del server

Il server dovrebbe (per cortesia) indicare nella risposta i tipi accettati con l'header Accept:

HTTP/1.1 415 Unsupported Media Type
Accept: application/json, application/xml

Questo permette al client di adattarsi dinamicamente senza leggere documentazione.

415 vs 406 Not Acceptable

  • 415: il server non accetta il Content-Type della richiesta (cosa il client INVIA).
  • 406: il server non puo produrre alcuno dei tipi richiesti nell'Accept (cosa il client VUOLE RICEVERE).

Implementazione in framework REST

Express.js:

app.use(express.json());  // accetta solo application/json
app.post('/api/users', (req, res) => {
    if (!req.is('application/json')) {
        return res.status(415).json({ error: 'JSON required' });
    }
    // ...
});

Laravel:

public function store(Request $request) {
    if (!$request->isJson()) {
        return response()->json(['error' => 'JSON required'], 415);
    }
}

Spring:

@PostMapping(value = "/users", consumes = "application/json")
public ResponseEntity<User> createUser(@RequestBody User user) {
    // Spring restituisce 415 automaticamente se Content-Type != application/json
}

Upload di file: white list MIME

Quando ricevi upload, validare il MIME type e fondamentale per sicurezza:

$allowed_mime = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($_FILES['file']['type'], $allowed_mime)) {
    http_response_code(415);
    exit('Solo immagini JPEG, PNG, GIF, WebP sono accettate');
}

Attenzione: il MIME inviato dal client e modificabile. Validalo anche server-side leggendo i magic bytes del file con finfo in PHP o file command in shell.

Content-Type vs Accept

I due header hanno ruoli simmetrici:

  • Content-Type: descrive il body INVIATO dal mittente.
  • Accept: descrive cosa il mittente accetta nella risposta.

Errori 415 derivano da Content-Type incompatibili; errori 406 da Accept incompatibili.

Charset e parametri

Il Content-Type puo includere parametri:

Content-Type: application/json; charset=utf-8
Content-Type: text/csv; charset=iso-8859-1; header=present
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

Server stricti possono restituire 415 se il charset non corrisponde a quello atteso, anche se il tipo base e corretto.

Debug pratico

curl -X POST -H "Content-Type: application/json" \
     -d '{"key": "value"}' \
     -i https://api.example.com/endpoint

Se ricevi 415, controlla:

  1. Il valore esatto di Content-Type (case sensitive in alcuni server).
  2. Presenza di charset corretto.
  3. Sintassi corretta del body (un JSON malformato puo essere segnalato come 415 anziche 400).
  4. Documentazione API per i tipi attesi.

Content negotiation

API moderne possono accettare più formati e adattarsi:

@PostMapping(value = "/users", consumes = {"application/json", "application/xml"})

Così la stessa endpoint accetta JSON o XML, scegliendo il parser in base a Content-Type.

Vary header

Se il tuo endpoint risponde diversamente in base a Content-Type, includi Vary: Content-Type per dire ai cache che la risposta dipende da quell'header.

Magic bytes validation

Il client puo dichiarare qualsiasi MIME type nell'header Content-Type, ma server attento valida via magic bytes (primi byte del file che identificano il formato). Esempio PHP: $mime = mime_content_type($tmp_path);. Esempio Python: magic.from_file(path, mime=True). Se MIME dichiarato != MIME reale, emetti 415 e logga il tentativo.

Charset utf-8 obbligatorio

API moderne dovrebbero richiedere application/json; charset=utf-8 e rifiutare altri charset con 415. UTF-8 e standard de facto, evita problemi di encoding misto e bug di serializzazione. Documentare nelle API docs.

Versioning via media type

Alcune API usano content negotiation per versioning: application/vnd.gtg.api.v2+json. Client invia Accept: vnd.gtg.api.v2+json, server risponde con quel content-type. Versioni non supportate: 406 (per Accept) o 415 (per Content-Type request).

Multipart edge cases

Form HTML con file usano multipart/form-data con boundary. Server che riceve application/x-www-form-urlencoded al posto di multipart deve emettere 415, perché non puo parsare file dal form encoding URL.

Differenze tra framework

Express richiede middleware (express.json, express.urlencoded). Senza, ogni POST con body diventa 400 o 415 a seconda. Laravel parsa automaticamente in base a Content-Type. FastAPI con Pydantic e molto strict: tipo MIME deve corrispondere al decoder Pydantic.

Esempi avanzati di MIME validation

Per file con polyfill MIME (es. SVG che puo contenere JS, ZIP-based formats come .docx/.xlsx), validation deve essere strict:

$tmp = $_FILES['file']['tmp_name'];
$mime = mime_content_type($tmp);
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));

$valid = [
  'image/jpeg' => ['jpg', 'jpeg'],
  'image/png' => ['png'],
];
if (!isset($valid[$mime]) || !in_array($ext, $valid[$mime])) {
    http_response_code(415);
    exit;
}

Content sniffing in browser

Browser fanno "MIME sniffing": se MIME e generico (text/plain) ma il contenuto sembra HTML, lo trattano come HTML. Pericoloso per XSS. Mitigazione: header X-Content-Type-Options: nosniff nelle risposte.

415 in webhook receivers

Webhook providers spesso inviano JSON. Se il tuo receiver supporta solo XML, 415 e corretto. Documenta nei meta della tua API webhook quali content-type accetti, e fornisci esempi chiari.

Hai bisogno di aiuto?

Se il tuo sito mostra errori HTTP, il team di G Tech Group puo aiutarti. Contattaci tramite il modulo di contatto.

Hai trovato utile quest'articolo?