Ошибка 415 Unsupported Media Type: как сверить Content-Type и тело запроса

Порядок диагностики ошибки 415 для формы, загрузки файла и API: сверка заголовков с телом запроса, проверка сжатия и поиск слоя отказа.

Ошибка 415 Unsupported Media Type означает, что сервер отказался обработать запрос из-за неподдерживаемого формата его содержимого. Сначала сверьте три вещи: какой формат принимает конкретный адрес API, что указано в заголовке Content-Type и какие данные действительно отправлены.

Ниже — порядок проверки для вебмастера или разработчика, у которого перестала отправляться форма, загрузка файла или запрос интеграции.

1. Найдите запрос, который получил 415

Откройте инструменты разработчика → Network («Сеть»), выполните проблемное действие на тестовом стенде и выберите запрос с кодом 415. Смотрите его URL, метод, Request Headers и Payload: главная страница может отвечать успешно, пока отдельный POST-запрос отклоняется.

Зафиксируйте время с часовым поясом и безопасный идентификатор запроса, если он есть. В ответе проверьте подсказку об ожидаемом типе.

Не публикуйте Cookie, Authorization, токены из URL и тело с данными клиента; повтор оплаты или заказа ради диагностики может создать реальную операцию.

2. Сверьте формат с контрактом API

Контракт — документация именно этого адреса и метода, а не общая инструкция для всего сервиса. Один маршрут может принимать JSON, другой — поля формы, третий — файл отдельным потоком.

| Ожидаемый формат | Заголовок Content-Type | Что должно быть в теле | |---|---|---| | JSON | application/json | JSON, например объект с полями | | Обычная форма | application/x-www-form-urlencoded | Пары вида key=value с URL-кодированием | | Форма с файлом | multipart/form-data; boundary=… | Части с разделителем boundary | | Двоичный поток | Тип, разрешённый контрактом | Байты файла без обёртки формы |

Если контракт требует специальный тип вроде application/vnd.example+json, используйте его: поддержка application/json не подразумевается.

Content-Type описывает отправляемое содержимое. Accept в запросе сообщает, какой ответ нужен клиенту; замена Accept не исправляет формат тела. Ошибка 406 относится к другой задаче — выбору приемлемого представления ответа.

По RFC 9110, раздел 15.5.16, причиной 415 может быть также Content-Encoding или проверка самих данных. В ответе Accept иногда подсказывает допустимые типы, а Accept-Encoding — допустимое сжатие запроса; отсутствие этих подсказок не отменяет ошибку.

3. Проверьте свою ветку

JSON: заголовок и сериализация должны совпадать

Сравните фактическое тело с ожидаемым JSON. Строка вида key=value остаётся данными формы, даже если сверху написать application/json; объект JavaScript перед отправкой через fetch обычно нужно сериализовать с JSON.stringify.

Проверьте, не превратился ли объект в строку "[object Object]" и не применили ли JSON.stringify дважды.

Ошибочный синтаксис или недопустимые поля могут вызвать другие коды, например 400 или 422: точный выбор зависит от приложения. Не пытайтесь определить единственную причину только по номеру статуса — читайте ответ и серверные логи.

Файл: не задавайте boundary вручную

При передаче браузерного FormData через fetch или XMLHttpRequest уберите самостоятельно заданный Content-Type. Браузер сформирует multipart/form-data вместе с boundary, который соответствует разделителям в теле; вручную оставленный заголовок без него ломает эту связку. Это правило описано в документации MDN о FormData.

Проверьте имя поля файла и допустимый тип каждой части по контракту. Если API ожидает необёрнутые байты изображения, отправка multipart-формы всё равно будет неверной; переименование расширения файла его содержимое не меняет.

Сжатие: проверьте Content-Encoding запроса

Если клиент добавляет Content-Encoding: gzip, выясните, поддерживает ли этот маршрут сжатое тело и действительно ли оно сжато. Для сравнения на стенде отправьте тот же допустимый документ без сжатия и без этого заголовка.

Не путайте это с gzip или Brotli в ответе сервера: здесь проверяется содержимое, которое клиент отправляет серверу. Не отключайте сжатие всего сайта ради одной операции.

4. Сделайте минимальный контрольный запрос

На тестовом адресе, который по документации принимает POST с JSON и не запускает реальные операции, можно проверить заголовок и небольшой документ отдельно от интерфейса:

curl -i 'https://example.test/api/validate' \
  -H 'Content-Type: application/json' \
  --data-binary '{"test":true}'

Адрес и поле условные: замените их разрешённым тестовым маршрутом и данными из его контракта. Пример не проходит авторизацию и не должен отправляться в боевой endpoint оплаты, заказа или рассылки.

В документации curl указано, что --data-binary сохраняет передаваемые данные, но его тип по умолчанию — application/x-www-form-urlencoded. Поэтому для JSON заголовок задан явно; curl не проверяет соответствие тела контракту API.

Проверяйте по очереди формат с соответствующим заголовком, сжатие и версию маршрута. Если контрольный запрос проходит, а форма нет, сравните их фактические запросы, включая конечный URL после перенаправления.

5. Найдите слой отказа и подтвердите исправление

Если корректный по контракту запрос всё ещё получает 415, сопоставьте его время и идентификатор в логах прокси и приложения. Если запись есть у прокси, но отсутствует у приложения, проверьте шлюз или фильтр перед ним. Убедитесь, что смотрите нужный узел и журнал.

Если запрос дошёл до приложения, проверьте обработчик маршрута, включённые парсеры тела и изменения после релиза. Для этого пригодится руководство как найти запрос в логах сервера; не расширяйте список принимаемых форматов без проверки последствий.

Исправление подтверждается исходным сценарием: данные приняты, получен ожидаемый результат и не появились лишние записи. Успешный GET главной страницы или health endpoint не проверяет проблемную отправку; границы таких проверок разобраны в статье о мониторинге API.

Следующий шаг

Если нашли несоответствие заголовка и тела, исправьте его в клиенте и добавьте проверку этого формата в тесты интеграции. Отдельный контроль доступности URL полезен для наблюдения за сервисом, но не заменяет тест отправки данных.

Если слой отказа остаётся непонятным, через форму Web-Puls можно отправить заявку на диагностику: укажите публичный адрес, время, действие, ожидаемый формат и безопасное описание ответа. Формат работ и стоимость согласуют до начала работ; пароли и полное тело запроса для первичной оценки не нужны.

Проверьте свой сайт прямо сейчас

Введите адрес сайта: Web-Puls покажет HTTP-код, время ответа и базовую диагностику. Для постоянного контроля можно подключить мониторинг.

Нужна помощь с диагностикой ошибки?

Опишите симптомы в короткой заявке: URL, код ответа, время появления и что менялось перед сбоем. Специалист поддержки оценит задачу и предложит формат работ.