Открываете файл - а там вместо текста сплошные кракозябры. Знакомо? Я сам сталкивался с этим не раз: переносишь сайт на новый хостинг, и русского контента больше нет - только непонятные наборы символов и вопросительные знаки. Браузер отображает страницы с иероглифами там, где должны быть заголовки. Причина не в вирусе и не в сбое - это конфликт кодировок.
Меня зовут Андрей Зенков, я руковожу веб-студией «Мельница» с 2013 года. За это время я разбирал сотни ситуаций, где текстовые данные оказывались нечитаемыми из-за банальной путаницы с кодировками. В этой статье объясню, как компьютер хранит текст, почему существует Windows-1251, как связаны ascii и unicode, и что делать, когда кракозябры снова появятся на экране. Всё это встречается в повседневной разработке - в HTML-разметке, в настройках серверов, в операционных системах.
Кодировка - это таблица соответствий: какой числовой код называется каким символом. Браузер определяет кодировку страницы по заголовку от сервера или по мета-тегу charset в HTML - если информация неверная или отсутствует, вместо текста появляется бессмыслица.
Пройдём путь от азов до практики: как устроен ASCII, зачем появилась Windows-1251, что такое Unicode и когда лучше выбирать UTF-8. Углублённое программирование оставим в стороне - только то, что реально нужно разработчику и владельцу сайта.
Как компьютер хранит текст - биты, байты и таблица символов
Главное, что нужно понять: компьютер не знает ни букв, ни картинок, ни звуков. Он работает только с числами - точнее, с последовательностями нулей и единиц. Любые данные, которые хранятся на диске или передаются по сети, существуют в бинарном формате. Это фундамент, без которого кодировки не имеют смысла.
Минимальная единица информации - бит. Один бит принимает два значения: 0 или 1. Один байт состоит из восьми бит. С помощью одного байта можно закодировать 256 различных значений - от 0 до 255 (это 2^8). Именно поэтому один байт долгое время был стандартной единицей для хранения одного символа текста.
Теперь возникает вопрос: как компьютер понимает, что число 65 - это буква «A», а не просто число 65? Ответ - таблица символов. Это справочник, где каждому числовому коду сопоставлен конкретный символ. Например, в большинстве стандартных таблиц число 65 соответствует заглавной латинской «A», а 97 - строчной «a». Без согласованной таблицы символов одна программа запишет в файл число 65 как «A», а другая прочитает его как что-то совершенно иное.
Для компактной записи кодов символов часто используют шестнадцатеричную систему счисления. В десятичной системе у нас цифры 0-9. В шестнадцатеричной добавляются буквы A-F, чтобы представить значения от 10 до 15 одним знаком. Число 65 в шестнадцатеричном (hex) формате записывается как 41. Число 255 - как FF. Это стандартный способ представления байтов в коде, в дампах памяти, в hex-редакторах.
Покажу на конкретном примере из практики. Когда я открываю дамп базы данных в hex-редакторе, вижу именно шестнадцатеричные коды символов. Для text-файла в кодировке UTF-8 русская буква «А» (заглавная) хранится как два байта: D0 90. В Windows-1251 та же буква «А» - это один байт: C0. В обоих случаях компьютер хранит числа, а не букву. Вот почему при смене кодировки текст рассыпается: программа читает числа через «не ту» таблицу символов и показывает не те значения символов.
Запомните главное: кодировка - это и есть таблица соответствий между числами и символами. Компьютер хранит числа. Программа должна знать, через какую таблицу их читать. Если таблица не совпадает с той, через которую данные записывались - текст может быть закодирован правильно, но отображаться как полная бессмыслица. Вот и вся механика кракозябр.
ASCII - american standard code for information interchange
В 1963 году в США появился стандарт, который на десятилетия определил развитие компьютерной индустрии. Его создали для передачи текстовых данных через телетайпы - устройства, которые печатали сообщения по телефонным и телеграфным линиям. Стандарт назвали ASCII, и произносится он как «аски».
Аббревиатура расшифровывается так: American Standard Code for Information Interchange - «американский стандартный код для обмена информацией». Один из авторов стандарта - Боб Бемер, инженер IBM, который работал над унификацией компьютерных систем того времени. До появления единого standard code for information exchange разные компании использовали несовместимые кодировки, и передача текста между устройствами была настоящей головной болью.
ASCII содержит всего 128 символов - от 0 до 127. Они делятся на две группы:
- Управляющие символы (0-31 и 127): непечатные коды, которые управляют поведением устройства. Код 10 - перевод строки (Line Feed), код 13 - возврат каретки (Carriage Return), код 7 - звуковой сигнал (Bell). Разработчики встречают их постоянно, особенно при работе с файлами из разных операционных систем.
- Печатные символы (32-126): пробел, цифры от 0 до 9, буквы латиницы (прописные и строчные), набор знаков пунктуации и специальных символов - @, #, $, %, & и другие.
Важная деталь в устройстве таблицы: латинские буквы расположены в строгом алфавитном порядке. Заглавная «A» имеет код 65, «B» - 66, «C» - 67 и так далее до «Z» с кодом 90. Строчные буквы идут следом: «a» - код 97, «b» - 98. Это не случайность - такой порядок упрощает сортировку текста. Достаточно сравнивать числовые коды, и слова выстроятся в алфавитном порядке автоматически, без дополнительной логики в программе.
В 1986 году ascii был принят организацией ANSI как официальный american standard - стандарт ANSI X3.4-1986. С тех пор он стал стандартом для большинства современных кодировок: первые 128 символов в UTF-8 полностью совпадают с ASCII. Это обратная совместимость, которая сохраняется по сей день. Именно поэтому сайты на чисто английском тексте работали без проблем при любой кодировке, а сложности начинались ровно тогда, когда нужна была кириллица.
Но у ASCII есть очевидный недостаток, который и привёл к появлению множества национальных кодировок. 128 символов хватает для английского языка, однако в таблице нет ни кириллицы, ни немецких умляутов, ни французских диакритических знаков. Каждая страна начала создавать собственное расширение: Германия - своё, Франция - своё, СССР и Россия - своё. Так появился целый зоопарк несовместимых стандартов, и ASCII превратился из универсального решения в фундамент, на котором каждый строил что-то своё. Именно из этого хаоса и выросла Windows-1251 - о ней поговорим в следующем разделе.
Кодировка Windows-1251 - таблица и структура
Windows-1251 появилась в 1990-1991 году - её создали совместно компании Параграф, Диалог и Microsoft Russia специально для русских версий Windows. С тех пор она стала стандартной кодировкой в операционных системах Windows вплоть до версии 10, где Microsoft окончательно перешла на Unicode.
По устройству windows 1251 - классическая 8-битная кодировка. Один байт кодирует один символ, итого 256 позиций. Первые 128 символов (диапазон 0x00-0x7F в шестнадцатеричной записи) полностью совпадают с ASCII - латиница, цифры, знаки препинания. Это сделано намеренно: файлы с чисто английским текстом совместимы с обеими системами без конвертации.
Вторые 128 позиций (диапазон 0x80-0xFF) - набор расширенных символов, уникальный для кодировки Windows. Здесь расположены кириллические буквы в алфавитном порядке: заглавные А-Я начиная с кода 0xC0, строчные а-я с кода 0xE0. Плюс несколько дополнительных позиций: №, знаки валют, типографские кавычки и тире.
Именно здесь cp1251 принципиально отличается от своей предшественницы CP866, разработанной для DOS-терминалов. В CP866 часть таблицы символов была занята псевдографикой - символами для рисования рамок в текстовом режиме. Windows-1251 от псевдографики отказалась и поставила на её место типографские символы. KOI8-R - альтернативная кириллица для Unix-систем и email - размещает русские буквы по принципу звучания: А там, где у латиницы A, Б там, где B. ISO-8859-5 тоже содержит символы кириллицы, но со сдвигом ровно на 16 байт относительно Windows-1251. Windows-1251 стала доминировать именно потому, что была стандартом для массовых настольных компьютеров на Windows - а их в 1990-е было большинство.
Ниже - таблица символов Windows-1251 для диапазона 0x80-0xFF. Ключевые позиции, которые реально нужны при работе с кириллицей в кодировке Windows:
| DEC | HEX | Символ | Описание |
| 128 | 80 | Ђ | Сербская буква Дже |
| 129 | 81 | Ѓ | Македонская буква Гже |
| 168 | A8 | Ё | Заглавная Ё |
| 184 | B8 | ё | Строчная ё |
| 185 | B9 | № | Знак номера |
| 186 | BA | є | Украинская строчная є |
| 192 | C0 | А | Заглавная А - начало кириллицы в алфавитном порядке |
| 193 | C1 | Б | Заглавная Б |
| 194 | C2 | В | Заглавная В |
| 195 | C3 | Г | Заглавная Г |
| 196 | C4 | Д | Заглавная Д |
| 200 | C8 | З | Заглавная З |
| 207 | CF | П | Заглавная П |
| 215 | D7 | Х | Заглавная Х |
| 220 | DC | Ь | Заглавная Ь |
| 223 | DF | Я | Заглавная Я - конец заглавных букв |
| 224 | E0 | а | Строчная а - начало строчных |
| 225 | E1 | б | Строчная б |
| 239 | EF | п | Строчная п |
| 240 | F0 | р | Строчная р |
| 255 | FF | я | Строчная я - конец кириллического блока |
Такая структура объясняет практическое удобство кодировки: кириллические символы расположены строго в алфавитном порядке, поэтому байтовое сравнение строк сразу даёт правильную сортировку. Если ваш сайт работал на Windows-сервере и использовал стандартную кодировку Windows по умолчанию, база данных и все текстовые файлы, скорее всего, тоже были в Windows-1251. Это важно понимать при миграции на Linux-хостинг или переходе на UTF-8.
Unicode и UTF-8 - зачем понадобилась универсальная кодировка

К концу 1980-х хаос с кодировками достиг предела. Десятки несовместимых стандартов по всему миру: для японского - Shift-JIS и EUC-JP, для арабского - ISO-8859-6, для кириллицы - KOI8-R, CP866, Windows-1251 и ещё несколько. Передать документ между системами без потерь было настоящей проблемой. Это и стало причиной создания универсальной кодировки, которую поддерживают все без исключения.
В 1991 году появился unicode - единый стандарт, который поддерживают все современные платформы: операционные системы, браузеры, языки программирования. Его задача - охватить все языки мира в одной таблице. Сейчас в Unicode больше миллиона кодовых позиций (точнее - 1 114 112), хотя реально занято из них около 150 тысяч. Здесь есть всё: латиница, кириллица, иероглифы, арабский, эмодзи, исторические письменности.
Важно понимать: unicode - это только таблица «символ - номер». А как этот номер записывается байтами - определяет конкретная utf-кодировка. Их несколько:
- UTF-8 - переменная длина от 1 до 4 байт на символ. Латиницу и ASCII кодирует одним байтом, кириллица занимает 2 байта на каждый символ, редкие иероглифы - 3-4 байта. Именно это сделало UTF-8 стандартом веба: английский текст весит столько же, сколько в ASCII.
- UTF-16 - 2 байта на большинство символов, кодирует до 65 536 символов в базовой плоскости. Используется внутри Windows и в Java.
- UTF-32 - всегда 4 байта на символ, файлы в 4 раза тяжелее UTF-8 для обычного текста. Применяется редко, в основном для внутренней обработки.
Теперь о том, что реально создаёт проблемы - BOM. Byte Order Mark - это специальные сигнатуры в начале файла (EF BB BF для UTF-8), которые сообщают программе: «перед тобой UTF-8». Звучит разумно. На практике - источник боли. php интерпретирует BOM как текст и выводит его перед DOCTYPE. Браузер видит этот мусор до тега Content-Type - и в итоге браузер покажет ошибку вместо нормальной страницы. Результат: сломанные HTTP-заголовки, кривое отображение, иногда полный отказ скрипта.
Моя рекомендация: всегда при сохранении файла выбирать UTF-8 без BOM. Большинство современных редакторов поддерживают оба варианта - просто нужно выбрать правильный. Проверить просто: открой файл в hex-редакторе и посмотри первые три байта. Если видишь EF BB BF - это BOM, его нужно убрать.
Главное практическое следствие: когда сервер отдаёт страницу, он указывает в HTTP-заголовке кодировку файла. Браузер открывает файл в той кодировке, которую получил от сервера. Если они не совпадают - вместо текста увидите кракозябры. Об этом подробно - в следующем разделе.
Кракозябры - почему возникают и как исправить
Кракозябры - это всегда одно и то же: браузер или программа читают байты файла не в той кодировке, в которой они записаны. Вот как это происходит. Браузер определяет кодировку по заголовку от сервера в HTTP-ответе или по мета-тегу charset в html-документе. Открыть файл в той кодировке, которую ему передал сервер - именно так и работает браузер. Если информация, которую ему передал сервер в заголовке, неверна или мета-тег отсутствует - браузер угадывает. И угадывает не всегда правильно. В каком месте не выдерживает эта цепочка соответствий - там и появляются кракозябры.
На практике я встречал три типичных сценария:
- Браузер показывает кракозябры на страницах сайта. Сервер отдаёт неверный charset или мета-тег отсутствует в html. Браузер пытается угадать кодировку самостоятельно.
- Текстовый редактор не может корректно открыть файл. Выбрана не та кодировка при открытии. Файл физически нормальный, просто программа читает его неверно.
- PHP или база данных возвращают кракозябры. Соединение с MySQL открыто без SET NAMES - данные проходят через неправильное преобразование.
Теперь конкретное исправление для каждого случая.
HTML. В блок <head> добавьте первой строкой:
<meta charset="UTF-8">
Именно первой - браузер читает html последовательно, и если charset объявлен позже других тегов, решение о кодировке может быть принято раньше, чем браузер до него дойдёт.
PHP. До любого вывода - до echo, до пробела - добавьте:
Строка для вставки: header('Content-Type: text/html; charset=utf-8');
Так сервер передаёт браузеру явный сигнал о кодировке в заголовке. Без этой строки сервер отдаст кодировку по умолчанию, которая может не совпадать с кодировкой файла.
Notepad++ (это тот самый блокнот для разработчиков, который стоит у каждого в закладках). Чтобы открыть файл и проверить его кодировку: перейди во вкладку Encoding в верхнем меню. Там кодировку файла он показывает текущей галочкой - файл отображается именно в той кодировке, которую считал редактор. Если надо перекодировать - нажать «Convert to UTF-8» и затем сохранить файл через Ctrl+S. Инструменты конвертации в Notepad++ не меняют файл на диске автоматически - только после сохранить.
.htaccess. Если управляешь сервером Apache, добавь в корневой .htaccess:
AddDefaultCharset utf-8
Сервер будет сам добавлять правильный charset в заголовке для всех файлов. Хорошее системное исправление, если не хочешь прописывать charset в каждом php-скрипте.
MySQL. Сразу после открытия соединения:
SET NAMES 'utf8mb4';
Важно: используй именно utf8mb4, а не просто utf8 - в MySQL utf8 это урезанная кодировка без поддержки 4-байтовых символов (эмодзи и часть редких знаков). Или используй инструменты уровня PDO - там charset прописывается прямо в строке DSN при подключении.
Расскажу случай из практики - разбора вживую лучше теории. Несколько лет назад обратился клиент с интернет-магазином на самописной CMS. Кракозябры только в описаниях товаров - всё остальное на страницах вижу нормально, заголовки, меню, блоки - всё корректно. Сайт в UTF-8, база в UTF-8, html объявление правильное. Час разбирались. Оказалось: менеджер загружал описания через старый импорт-скрипт, который открывал соединение с базой без SET NAMES. Данные писались в таблицу с неверным преобразованием. Выгрузили, перекодировали через iconv, залили обратно - и никаких глюков все работает именно так, как должно. Типичный пример: цепочка почти корректно настроена, но одно звено пропущено.
Главное правило: если всё настроено корректно на каждом уровне - файл, сервер, html-объявление, соединение с базой - глюков все работает именно без сюрпризов. Кракозябры всегда указывают на конкретное звено, где кодировки разошлись. Найдите это звено - и исправление будет простым.
Windows-1251 или UTF-8 - что выбрать

Меня часто спрашивают: какую кодировку выбрать для нового проекта? Ответ простой - UTF-8 без BOM. Всегда. Для новых проектов это единственный разумный выбор, и объясню почему.
UTF-8 сегодня используется на более чем 97% сайтов в интернете. Все современные браузеры, фреймворки, библиотеки поддерживают её по умолчанию. PHP начиная с версии 5.6 ориентирован на UTF-8 (а сейчас актуален PHP 8.x, где это уже давно умолчание). Если вы создаёте API, мобильное приложение, мультиязычный сайт - UTF-8 это стандарт, а не вопрос предпочтений. Любой другой выбор создаст головную боль при интеграции с внешними сервисами, которые ожидают именно его.
Но есть ситуации, когда Windows-1251 вообще не ошибка, а вынужденная реальность. Речь про legacy-системы: старые базы данных, государственные сервисы с фиксированным форматом обмена, корпоративные системы 2000-х годов, старый импорт-скрипт, которого никто не трогал 10 лет. Если проект на Windows-1251 работает нормально без сбоев - не трогайте. Сначала посмотреть на последствия миграции, потом принять решение. Это самый распространенный совет, который я даю клиентам: работает - не чини.
Теперь про момент, который снимает половину тревог. Если браузер получает файл с правильно объявленной кодировкой в HTML-заголовке и на сервере - тоже похер на кодировку файла в каком-то абстрактном смысле. Неважно, Windows-1251 или UTF-8 - если объявлено корректно везде, никаких глюков все работает. Проблемы начинаются именно при смешении: файл в одной кодировке, объявление в другой, база в третьей. Вот тогда кракозябры и вылезают.
Отдельный момент про разработку на локальном компьютере. Кодировка локального компьютера должна быть согласована с кодировкой сервера. Если редактор на вашей машине сохраняет файл в Windows-1251, а сервер ожидает UTF-8 - при загрузке получите проблемы. Поэтому настройте редактор один раз: UTF-8 без BOM для сохранения файлов, и забудьте об этом навсегда.
Что делать, если нужно преобразовать существующий проект с Windows-1251 на UTF-8? Здесь главное - не делать это частично. Подходит только полная миграция: конвертируете все файлы, конвертируете базу данных, меняете объявления в HTML и PHP-конфигурации, проверяете настройки сервера. Если сделать только часть - например, конвертировать только файлы, но забыть про базу - получите ровно тот же результат, что и до миграции, только с другими кракозябрами.
Практический итог от меня лично: новый проект - сразу UTF-8 без BOM, это решается один раз в настройках редактора и сервера. Существующий проект на Windows-1251 работает - оставьте как есть, не создавайте риски. Мигрируете - делайте всё и сразу, проверяйте каждый уровень. Половина кракозябр в моей практике появлялась именно из-за частичной конвертации, когда программист файлы конвертировал, а про базу данных забыл.
Данная статья основана на личном опыте автора и актуальна на момент публикации. Интерфейсы сервисов и алгоритмы поисковых систем регулярно меняются - рекомендую проверять актуальность инструкций на официальных ресурсах. Если у вас остались вопросы - задайте их в комментариях.
Список литературы
- Консорциум Unicode. The Unicode Standard.
- Международная организация по стандартизации. ISO/IEC 8859-5: Information technology - Character sets. ISO.
- Яндекс. Кодировка сайта. Яндекс.Помощь для вебмастеров.
- Кодировка текста ASCII, Windows-1251, CP866, KOI8-R и Юникод UTF-8, 16, 32 - как исправить проблему кракозябров. JavaRush, 2018.












Спасибо за статью