← Все статьи

SiteGuard12 мин чтения

ERR_CONTENT_DECODING_FAILED: как исправить ошибки сжатия ответа сайта

Сайт отвечает, но браузер показывает ERR_CONTENT_DECODING_FAILED, страница остаётся пустой или не загружается важный скрипт. Такой сбой способен остановить оформление заказа даже при статусе 200 в журнале сервера. Клиент получил ответ, однако не смог восстановить из переданных байтов содержимое, которое должен использовать.

Чтобы исправить ошибку сжатия ответа сайта, нужно сопоставить заголовки, фактическое тело и преобразования на пути от приложения до браузера. Ниже — последовательность проверки для владельца сайта и разработчика: от проблемного URL до повторного испытания после исправления. Адреса, размеры и результаты примеров учебные; они показывают метод диагностики, а не показатели конкретного проекта.

Что означает ERR_CONTENT_DECODING_FAILED

При HTTP-сжатии сервер отправляет компактное представление ресурса и сообщает способ его декодирования. Согласно описанию Content-Encoding на MDN, этот заголовок перечисляет применённые кодирования. Например, значение gzip говорит получателю, как восстановить исходные данные. Оно не меняет назначение ресурса: сжатый JavaScript остаётся JavaScript.

Ошибка возникает, когда обработка полученного представления не удаётся. Возможные направления расследования — неверный заголовок, повреждённый или оборванный поток, несогласованные преобразования на промежуточном узле. Само сообщение браузера не устанавливает, какой сервер или фильтр виноват. Для вывода нужны сохранённый ответ и сравнение на границах инфраструктуры.

Не путайте сжатие с кодировкой символов. Неправильный UTF-8 может проявляться искажённым текстом, но смена charset не исправит повреждённый gzip-поток. Ошибка MIME-типа и HTML вместо JSON также требуют своих проверок. Сначала выясните, удалось ли декодировать тело, затем проверяйте, соответствует ли результат ожидаемому формату.

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

Сопоставьте Accept-Encoding, Content-Encoding и Content-Type

Заголовок запроса Accept-Encoding сообщает, какие способы кодирования клиент принимает. Сервер выбирает вариант и описывает фактическое преобразование через Content-Encoding. Как поясняет документация Accept-Encoding, предложение сжатия не обязывает сервер его применять: при допустимом варианте без сжатия ответ может остаться несжатым.

Для диагностики сохраните оба заголовка, а также Content-Type, статус и URL конечного ответа. Нельзя заключить, что тело сжато gzip, только потому, что клиент попросил gzip. Проверяйте ответ. Аналогично расширение файла .gz не заменяет анализ HTTP-заголовков и назначения скачиваемого объекта.

Архив как самостоятельный файл и HTTP-сжатие обычной страницы — разные случаи. Если посетитель скачивает архив, его внутреннее сжатие является частью формата файла. Добавление Content-Encoding: gzip к любому архиву без понимания назначения ответа может заставить клиента обработать данные иначе, чем ожидает приложение.

Запишите результаты в короткую карточку инцидента: «запрошен ресурс версии R8, клиент предлагает gzip, получен 200, объявлен gzip, декодер сообщает повреждение». Такое описание оставляет проверяемые факты. Формулировка «сломался браузер» заранее назначает виновного и мешает сопоставить наблюдение с конфигурацией доставки.

Найдите конкретный ресурс и условия сбоя

Откройте инструменты разработчика до воспроизведения проблемы и найдите отказавший запрос во вкладке Network. Проверяйте адрес из сообщения об ошибке: основной HTML, скрипт, стили или ответ API. Если страница использует несколько доменов, у проблемного ресурса может быть собственный CDN и совершенно другой путь публикации.

Зафиксируйте время, браузер, страницу-источник, состояние авторизации и версию ресурса. Запишите, происходит ли сбой при первом открытии, повторном посещении или только после релиза. Сравнение имеет смысл, когда меняется один фактор. Одновременная смена сети, аккаунта и адреса даст другой результат, но не объяснит его.

Отдельно отметьте, использовалась ли сохранённая копия. Для свежего сетевого запроса условия должны быть явно заданы и отражены в записи. Ответ 304 без нового тела не подходит для самостоятельной проверки целого сжатого ресурса. Сохраните также исходный проблемный ответ, если он доступен, чтобы очистка кеша не уничтожила единственное свидетельство.

Если сбой наблюдается только у одного посетителя, сравните одинаковый URL в контролируемом чистом профиле браузера и другой разрешённой среде. Это помогает выделить влияние расширения или локального посредника. Не отключайте защитные средства организации ради эксперимента: передайте различие условий ответственному специалисту и повторите проверку согласованным способом.

Сравните ответ без сжатия и с декодированием

Выберите небольшой публичный тестовый ресурс собственного сайта с известным содержимым и стабильной версией. Следующие команды рассчитаны на установленный curl и отдельную папку диагностики. Подставьте фактический HTTPS-адрес ресурса; имена файлов используются для раздельного сохранения результатов.

curl --silent --show-error --max-time 30 --dump-header identity.headers --header 'Accept-Encoding: identity' --output identity.body 'https://shop.example/assets/diagnostic.txt'

curl --silent --show-error --max-time 30 --compressed --header 'Accept-Encoding: gzip' --dump-header decoded.headers --output decoded.body 'https://shop.example/assets/diagnostic.txt'

В первом запросе предлагается вариант без сжатия. Во втором выбран gzip и включено декодирование curl. Возможность запросить сжатый ответ и распаковать его до сохранения описана в руководстве everything curl. Всё равно проверяйте фактический Content-Encoding: сервер может вернуть несжатое представление.

Команды намеренно сохраняют заголовки отдельно от тела и ограничивают продолжительность испытания. Они не следуют перенаправлениям автоматически: если пришёл 3xx, сначала выясните конечный адрес и назначение перехода. Ответ страницы входа нельзя сравнивать с ожидаемым текстовым файлом. Для закрытого ресурса нужен отдельный согласованный тест с действующей авторизацией.

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

Проверьте именно сжатые байты

Для исследования gzip-потока сохраните ещё один ответ без автоматической распаковки. В этом запросе отсутствует --compressed; это отдельная диагностическая проба, а не файл, полученный предыдущей командой.

curl --silent --show-error --max-time 30 --header 'Accept-Encoding: gzip' --dump-header encoded.headers --output encoded.gzip 'https://shop.example/assets/diagnostic.txt'

gzip -t encoded.gzip

Вторая команда применима, когда ответ действительно объявлен как gzip и в среде установлен GNU gzip. Его официальное руководство описывает -t как проверку целостности сжатого файла. Если сервер вернул HTML ошибки или несжатый текст, отказ gzip не доказывает повреждение компрессора: сначала сопоставьте статус, заголовки и тело.

Есть важная ловушка инструмента. По документации curl, при --compressed сохранённые заголовки не переписываются после декодирования. Поэтому обычный текст в decoded.body рядом с Content-Encoding: gzip в файле заголовков ожидаем и сам по себе не означает ошибку сервера.

Не добавляйте без необходимости --raw: он отключает также обработку транспортных кодирований HTTP, что меняет содержимое диагностического файла. Проверяйте сжатое представление подходящим декодером, а восстановленный результат — по ожидаемому содержимому. Исправный контейнер gzip вполне может содержать не тот документ.

Разберите повторное сжатие и несогласованные заголовки

Нарисуйте фактическую последовательность преобразований: что выдаёт приложение, что делает веб-сервер, какие изменения вносит прокси и что отправляет CDN. Рядом укажите, кто формирует заголовок Content-Encoding. Не ограничивайтесь поиском слова gzip в одном конфигурационном файле: сжатие может включать библиотека приложения или отдельный модуль публикации.

RFC 9110, раздел 8.4, допускает несколько кодирований и требует перечислять их в порядке применения. Следовательно, само наличие двух преобразований не является автоматическим нарушением протокола. Проблема возникает, когда фактическая цепочка и объявленное описание расходятся либо получатель не может обработать результат.

Типовые гипотезы: посредник распаковал тело, но оставил прежний заголовок; приложение добавило gzip-заголовок к обычной странице ошибки; фильтр повторно сжал данные, не согласовав описание преобразований. Каждую гипотезу подтверждают сравнением входа и выхода соответствующего узла. Совпадение по времени с установкой плагина лишь указывает направление проверки.

Не исправляйте ситуацию простым удалением заголовка при сохранении сжатых байтов. Браузер тогда может попытаться использовать их как исходный документ. Обратное действие — дописать gzip к любому ответу — также опасно. Исправление должно согласовать преобразование, тело и метаданные для всех веток выбранного обработчика.

Сравните origin и публичный маршрут

Если доступен разрешённый диагностический путь к исходному серверу, повторите одинаковые пробы до и после проблемного посредника. Сохраняйте имя сайта, TLS-проверку, путь, параметры и версию ресурса. Запрос к IP-адресу другого виртуального хоста не является эквивалентной проверкой и способен вернуть совершенно иной ответ.

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

Учитывайте, что отдельный диагностический адрес может обходить не только CDN, но и часть правил маршрутизации или авторизации. Сначала подтвердите сопоставимость ответов. Для динамической страницы используйте тестовые данные или стабильный диагностический ресурс: разные временные метки в теле не должны выглядеть как повреждение при сравнении.

Сверьте записи с журналами по времени и техническому идентификатору запроса. Сохраните версии конфигурации, узел обработки и факт использования кеша, если инфраструктура это позволяет. Не публикуйте исходные заголовки с cookies или подписанные URL в открытой задаче. Для обсуждения причины обычно достаточно обезличенной карточки и контролируемого тестового объекта.

Проверьте варианты кеша и Vary

По одному URL могут существовать несжатое и сжатое представления. Заголовок Vary сообщает, какие поля запроса повлияли на выбор ответа. Для соответствующего согласования кодирования используется Vary: Accept-Encoding. Проверяйте также фактические правила кеша и CDN: наличие заголовка само по себе не подтверждает правильную работу всех пользовательских настроек.

Чередуйте контролируемые запросы без сжатия и с gzip, записывая результат после первого заполнения кеша и при повторной выдаче. Если порядок запросов меняет результат, исследуйте разделение вариантов. Такой тест полезнее одного удачного открытия после полной очистки: он показывает, не возникает ли ошибка снова при обычной работе.

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

После устранения источника ошибки удалите именно затронутые некорректные объекты предусмотренной процедурой. Затем проверьте и первый ответ, и повторное попадание в кеш. Очистка без исправления генератора создаст короткий период улучшения, после которого та же неверная копия снова станет доступна посетителям.

Отделите повреждение потока от неправильного сравнения размеров

При наличии Content-Encoding значение Content-Length относится к закодированному представлению, что отдельно отмечает MDN. Поэтому нельзя сравнивать его непосредственно с длиной уже распакованного файла и объявлять разницу потерей данных. В карточке измерения всегда указывайте, какие именно байты считались: переданные сжатые или восстановленные.

Например, учебный ресурс после распаковки занимает 80 000 байт, а согласованное gzip-представление — 21 000 байт. Это нормальная разница форм. Если для конкретного полного ответа заявлено 21 000 байт сжатого тела, а получено только 14 000, нужно исследовать неполную передачу. Наличие корректных начальных строк не доказывает целостность оставшегося потока.

Сравнивайте результат с журналами приложения и посредников. Возможны преждевременное завершение генерации, разрыв соединения, ошибка чтения объекта или ограничение инфраструктуры. Не увеличивайте сразу все таймауты: сначала установите, на каком шаге остановилась выдача и какое событие это подтвердило. Декодер показывает следствие, а журнал может объяснить причину остановки.

Для проверки используйте целый небольшой объект. Диапазон байтов или обрезанный диагностический фрагмент нельзя безусловно передать декодеру как самостоятельный gzip-файл. Если полный тестовый объект проходит, а большой рабочий ответ нет, сравните условия генерации и доставки, сохранив воспроизводимый пример разумного размера.

Пример: ошибка появляется только после заполнения кеша

Предположим, после изменения правил CDN часть пользователей перестала получать JavaScript. Команда выбрала стабильный тестовый ресурс той же ветки публикации. Таблица ниже иллюстрирует наблюдения в учебной неисправной конфигурации; размеры не относятся к реальному сайту.

ПроверкаНаблюдениеСледующий шаг
Origin, запрос identityОжидаемый несжатый текстСохранить эталон содержимого
Origin, запрос gzipПоток целый, результат совпадает с эталономПроверить внешний участок
Публичный адрес после запроса identityВ кеше появился несжатый вариантПовторить запрос с gzip
Публичный адрес, запрос gzipВыдано несжатое тело с ошибочным заголовком gzipИсследовать правило заголовков и выбор кеш-объекта

Проверка конфигурации в этом примере выявила правило, которое добавляло gzip-заголовок по свойству запроса без проверки фактического ответа. Команда исправила правило и согласовала разделение вариантов. После удаления затронутых объектов она повторила последовательности identity → gzip и gzip → identity.

Исправление подтвердили согласованные заголовки, успешное декодирование и совпадение восстановленного содержимого. Пример показывает принцип доказательства: сначала наблюдение на границе, затем проверяемая причина и повторный тест. Он не означает, что любое подобное сообщение обязательно вызвано CDN или кешем.

Вносите исправление на найденном участке

Для Nginx проверьте, какой конфигурационный блок обрабатывает проблемный URL и где включён нужный фильтр. Документация ngx_http_gzip_module описывает управление gzip и директиву gzip_vary. Эти параметры относятся к работе соответствующего модуля; они не исправляют произвольное сжатие внутри приложения или на другом узле.

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

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

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

Как принять исправление и заметить повторение

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

  • Проблемный URL и версия ресурса однозначно установлены.
  • Фактическое тело соответствует объявленному кодированию и типу.
  • Полный поток проходит декодирование, а восстановленное содержимое соответствует ожиданию.
  • Публичная выдача проверена после заполнения кеша и при повторном обращении.
  • Успешные и ошибочные ветки обработчика сохраняют корректные заголовки.
  • Пользовательский сценарий снова выполняется, доказательства исправления сохранены.

Назначьте владельца правил доставки и добавьте этот набор в проверку релизов, которые затрагивают прокси, CDN или сжатие. Тогда следующий инцидент начнётся с готового сравнения, а не с повторного поиска всех настроек. Контроль доступности и проверка корректности тела дополняют друг друга: для каждой задачи нужен наблюдаемый критерий.

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