ERR_CONTENT_DECODING_FAILED означает, что браузер не смог декодировать тело ответа, например распаковать gzip или Brotli. Сервер при этом может вернуть 200 OK: успешный HTTP-код ещё не подтверждает, что страницу или скрипт удалось прочитать.
Сначала найдите точный запрос с ошибкой, затем сравните его загрузку без сжатия и со сжатием. Это помогает отделить повреждённый поток или неверный Content-Encoding от общего сбоя сайта, ошибки кэша и проблемы только на устройстве посетителя.
Что именно не удалось прочитать
В документации Chromium эта ошибка относится к декодированию тела ответа. Это сообщение браузера, а не HTTP-статус и не указание на неправильную кодировку русских букв.
Три заголовка отвечают за разные вещи:
- Accept-Encoding в запросе сообщает, какие способы сжатия принимает клиент.
- Content-Encoding в ответе описывает преобразования, применённые к телу, например gzip или br.
- Content-Type описывает тип содержимого; параметр charset относится к кодировке текста.
По RFC 9110, объявленные преобразования должны соответствовать переданному представлению. Если сервер отправил обычный HTML с Content-Encoding: gzip, клиент попытается распаковать несжатые данные и получит ошибку.
1. Найдите первый сбойный ресурс
Откройте инструменты разработчика → Network, включите сохранение журнала и Disable cache, затем перезагрузите страницу. Ищите запрос с ERR_CONTENT_DECODING_FAILED: это может быть документ, CSS, JavaScript или ответ API.
Запишите публичный URL, время и часовой пояс, HTTP-код, Content-Encoding, Content-Type и доступные заголовки кэша, например Age или X-Cache. Сравните тот же адрес в чистом профиле браузера и другой сети; флажок Disable cache сам по себе не исключает перехват ответа service worker.
Если ошибка есть только на одном устройстве, проверьте расширения, локальный прокси и фильтрацию HTTPS по правилам вашей организации. Очистка локального кэша не исправляет серверный дефект.
2. Сравните GET без сжатия и со сжатием
Для проверки нужен GET с чтением тела. HEAD через curl -I показывает заголовки, но не проверяет распаковку; используйте один и тот же конечный публичный URL без паролей и токенов.
В примерах замените адрес на свой ресурс. Команды рассчитаны на терминал Linux или macOS:
~~~bash curl -q -sS --max-time 30 -H 'Accept-Encoding: identity' -D identity.headers -o identity.body 'https://example.com/assets/app.js'
curl -q -sS --max-time 30 --compressed -H 'Accept-Encoding: gzip' -D gzip.headers -o gzip.body 'https://example.com/assets/app.js'
curl -q -sS --max-time 30 --compressed -H 'Accept-Encoding: br' -D br.headers -o br.body 'https://example.com/assets/app.js' ~~~
Проверьте Content-Encoding каждого фактического ответа, текст ошибки curl и код завершения команды. Запрос identity просит ответ без сжатия, но результат всё равно нужно сверить; если получен редирект, сначала найдите конечный URL и повторите сравнение на нём.
По руководству curl, --compressed включает декодирование поддерживаемого сжатия. Сохранённые заголовки при этом остаются исходными: файл gzip.body уже распакован, хотя gzip.headers может содержать Content-Encoding: gzip.
Перед тестом br проверьте curl --version: нужна поддержка Brotli. Ошибка неподдерживаемого алгоритма у локального curl не доказывает поломку сайта.
Как читать результат
- identity загружается, gzip не декодируется: исследуйте gzip-представление и слой, который его формирует.
- gzip работает, br не работает: проверьте Brotli, заранее сжатый файл и соответствующий вариант в CDN.
- Все варианты работают в curl, но браузер выдаёт ошибку: сравните реальные заголовки браузера, сеть, кэш и service worker. Запросы могли получить разные ответы.
- Передача обрывается и без сжатия: проверьте приложение, прокси и полноту ответа. Для расхождения длины есть отдельный разбор ERR_CONTENT_LENGTH_MISMATCH.
3. Найдите слой, который портит представление
Сопоставьте ответ приложения, reverse proxy и CDN для того же пути и Accept-Encoding. Origin — исходный сервер за CDN; сравнение с ним выполняет администратор через предусмотренный служебный доступ с правильными Host и SNI, без открытия origin наружу.
Проверьте три типичных сценария:
- Заголовок не соответствует телу. Приложение или прокси распаковал ответ, но оставил Content-Encoding; либо заранее сжатый файл отдан с неверным обозначением алгоритма.
- Неучтённое повторное сжатие. Несколько слоёв преобразуют тело, а заголовок описывает только один из них. Несколько преобразований допустимы при корректном объявлении.
- Смешались варианты кэша. Клиент получил тело одной версии и метаданные другой. Сверьте Vary: Accept-Encoding и правила выбора вариантов у вашего CDN; наличие заголовка не гарантирует правильную настройку всей цепочки.
Если origin исправен, а внешний ответ повреждён, исследуйте преобразования и кэш между ними. Подробная методика проверки свежести вынесена в статью об HTTP-кэше через curl.
4. Исправьте причину и проверьте повторную загрузку
Меняйте по одному условию: исправьте заголовки или настройки конкретного слоя, пересоберите повреждённый сжатый файл. Не удаляйте Content-Encoding у действительно сжатого тела: браузеру всё равно нужно знать, как его прочитать.
После исправления удалите только затронутый объект или вариант из CDN-кэша. Проверьте загрузку при холодном и прогретом кэше, с identity, gzip и br, затем убедитесь в браузере, что критичный ресурс используется без ошибки.
Регулярная внешняя проверка в Web-Puls помогает сохранить время и историю симптома, но не заменяет браузерную проверку распаковки и серверные логи. Контроль одного статуса 200 может пропустить такой дефект.
Если слой сбоя остаётся неясным, отправьте заявку на профессиональную поддержку: укажите публичный URL, время, результаты трёх вариантов и недавние изменения сжатия или CDN. Специалист оценит вводные и согласует формат работ; Cookie, Authorization и доступы в описание не добавляйте.