Белая страница при 200: как types в nginx заменяет всю mime-таблицу
vipsrv:~$ cat nginx-types-belaya-stranica.md

vipsrv:~$ cat nginx-types-belaya-stranica.md

# техничка // разбор своего пожара

Как одна строка nginx убила сайт целиком (и почему «чинится одной строкой» - ловушка)

Белый экран при коде 200

Есть особый сорт аварии: всё живо, а смотреть не на что. Не 404, не 500. Сайт бодро отдаёт 200, файлы лежат где лежали, сервер дышит ровно. А в браузере белый лист. Чистый такой, аккуратный. Хоть заметки на нём пиши.

Дело было 16 августа, на этом самом сайте. Ломала связка. Кода я не знаю, в конфиги не лезу - за мной задача и приёмка, руки тут - МиниМы с агентами. Строку вписал агент, по собственному отчёту «чинится одной строкой». Белую страницу поймал я.

Что было видно глазами

Правка была «безобидная» по словам агента - теперь это слово в отчётах связки всегда в кавычках. После неё сайт открылся белым листом. Все ассеты отвечают 200. В консоли браузера ни одной красной строки про сеть, просто стили не применяются. Файлы приходят. Стилей нет. Сижу, моргаю.

Антон: очень быстро без диза вообще на белой странице

МиниМы: Смотрю. Все ассеты 200, ошибок в консоли нет - а стили не применяются.

*(пауза на разбор)*

МиниМы: Нашёл, косяк наш: types в nginx не добавляет тип - подменяет всю таблицу. Была карта на 900 типов, стала на один woff2. Откатываю.

Одна строка, которая это устроила

Агент полировал сайт и в конце отчёта дописал хвост: шрифт woff2 отдаётся как octet-stream, чинится одной строкой в custom-настройках nginx. Строку применили дословно из отчёта, через 43 секунды после того, как он закрыл задачу. В custom-конфиг vhost ушло:

types { font/woff2 woff2; }

Задача была смешная: чтобы шрифт .woff2 отдавался с правильным Content-Type. Логика железная - одному расширению один тип. Что тут вообще может пойти не так.

Корень: types не добавляет, а замещает

Директива types в nginx НЕ ДОБАВЛЯЕТ тип к таблице. Она ЗАМЕНЯЕТ таблицу целиком.

Была карта на ~900 типов из mime.types. Стала карта из ОДНОГО woff2. Всё остальное - css, js, svg, png - поехало как application/octet-stream. Шрифт, кстати, действительно починился. Ценой девятисот позиций. Размен, за который в приличном обществе бьют по рукам.

Почему все проверки были зелёными

Код ответа у всех файлов - 200. Ни одного битого запроса. При этом браузер в standards mode ОТКАЗЫВАЕТСЯ применять css с неправильным Content-Type (nosniff-поведение), и делает это молча. Файл скачался, код 200, стилей нет. Никто никому ничего не сказал.

Проверка «все ассеты отвечают 200» была честной и совершенно бесполезной. Мерили не то, что сломалось.

Правильный фикс

Не types, а location:

location ~* \.woff2$ { default_type font/woff2; }

Точечно для одного расширения, вся остальная таблица живёт своей жизнью и меня не трогает.

Правильная приёмка

Не «все ассеты 200», а код плюс Content-Type ПО КАЖДОМУ ТИПУ - по css, js, svg, шрифту, html:

curl -o /dev/null -w "%{http_code} %{content_type}"

Пять секунд работы. Ровно та проверка, которая показала бы octet-stream там, где положен text/css, до того как пошли радоваться шрифту.

Мораль в три слоя

Слой первый, технический: types{} в nginx - подмена, не добавка. Нужен один тип - бери location и default_type.

Слой второй, и он тут главный: «чинится одной строкой» - это гипотеза о механизме, а не готовая правка. Строка, которая чинит одно, может молча заменить собой карту на 900 позиций. Перед применением надо замерить СОСЕДЕЙ по классу, а не только цель. Правишь mime-тип шрифта - меряй все mime-типы. Иначе выходит ровно мой 3.14здец: цель зелёная, вокруг выжженное поле, а метрика радостно рапортует «двести».

Слой третий, про связку: «чинится одной строкой» из отчёта агента - это гипотеза, произнесённая уверенным голосом. Проверять соседей по классу - работа того, кто принимает. В тот вечер не проверил никто.

У меня на этот счёт есть правило, которое я повторяю всем подряд и которое сам же в тот вечер нарушил: «или хорошо или ни как». Быстрая правка «на посмотреть» - это и есть «ни как», только с гарантией 200 в ответе. Знать и делать - разные мышцы, и вторая у меня в тот вечер отдыхала.

Что осталось в чек-листе

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

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

Антон Майорских, практик. Строку вписал агент, белую страницу увидел я, правило после разбора общее.

> конец записи