Вы добавили скрипт на сайт - и он не работает. Или работает, но страница грузится в два раза дольше. Или вообще выдает ошибку в консоли. Проблема не в коде. Проблема в том, где вы его разместили.
Почему место скрипта - это не просто деталь
Скрипт - это не просто строка кода. Это блокирующий ресурс. Когда браузер встречает <script>, он останавливает разбор HTML. Даже если скрипт маленький. Даже если он просто меняет цвет кнопки. Он ждёт: скачался ли файл? Выполнился ли код? Только потом продолжает рендерить страницу.
Если вы положили скрипт в <head> - пользователь видит пустой экран дольше. Если вы положили его в конец <body> - страница появляется быстро, а скрипт запускается потом. Это разница между тем, чтобы пользователь ушёл с сайта, и тем, чтобы он остался.
В 2026 году 73% пользователей покидают сайт, если он не загрузился за 3 секунды. А скрипт в неправильном месте - одна из главных причин задержки.
Скрипт в <head>: когда это работает
Есть случаи, когда скрипт должен быть в <head>.
- Код, который нужен для работы самого HTML - например, полифиллы для старых браузеров (Polyfill.io, ResizeObserver, IntersectionObserver).
- Скрипты, которые управляют отображением контента до его загрузки - например, скрипты для темной темы, которые должны применить класс
darkдо того, как пользователь увидит светлый фон. - Скрипты аналитики, которые должны начать собирать данные сразу, как только страница начинает загружаться.
Но даже в этих случаях используйте атрибут defer:
<script src="analytics.js" defer></script>
defer говорит браузеру: «Скачай скрипт параллельно с HTML, но не запускай его, пока разбор документа не завершён». Это значит - страница рендерится без задержек, а скрипт выполняется сразу после готовности DOM.
Скрипт в конце <body>: стандарт 2026 года
Большинство скриптов - это поведение. Они реагируют на клики, скролл, ввод данных. Им не нужно ждать, пока пользователь увидит страницу. Им нужно, чтобы страница была уже готова.
Вот правильное место для 90% скриптов:
<body>
<header>...</header>
<main>...</main>
<footer>...</footer>
<script src="main.js"></script>
</body>
Так браузер:
- Парсит HTML - строит DOM.
- Рендерит страницу - пользователь видит контент.
- Только потом скачивает и запускает скрипт.
Это не просто «хорошо». Это обязательно для скорости. В Google PageSpeed Insights скрипты в конце <body> - один из ключевых факторов оценки «быстрого» сайта.
Атрибуты async и defer: что выбрать?
Если скрипт большой - например, библиотека, фреймворк, рекламный код - используйте async или defer. Но не путайте их.
| Атрибут | Когда скачивается | Когда выполняется | Порядок выполнения | Подходит для |
|---|---|---|---|---|
async |
Параллельно с HTML | Сразу после скачивания | Нет - выполняются в порядке скачивания | Аналитика, реклама, сторонние скрипты |
defer |
Параллельно с HTML | После полного разбора HTML | Да - в порядке в коде | Ваш собственный JS, модули, приложения |
Пример: если вы используете React и загружаете его из CDN - ставьте defer. Если это Google Analytics - async.
Если вы не уверены - используйте defer. Он безопаснее. Он предсказуем. Он не ломает порядок выполнения.
Что делать с модулями JavaScript?
Если вы используете type="module":
<script type="module" src="app.js"></script>
Тогда defer включён по умолчанию. Вы не можете его отключить. И вы не можете использовать async вместе с type="module".
Модули всегда загружаются параллельно и выполняются после разбора DOM. Это идеально для современных приложений. Но это значит - вы не можете использовать модули в <head>, если вам нужно, чтобы они работали до рендера.
Если вы пишете SPA на React, Vue или Svelte - всё ваше приложение будет загружаться как модуль. Размещайте его в конце <body>. Это стандарт.
Плохие практики, которые ещё встречаются
Вот что делать не нужно:
- Вставлять скрипты прямо в тело HTML -
<script>document.getElementById(...)</script>. Это мешает кэшированию, усложняет отладку и ломает SEO. - Ставить скрипты после
</html>. Это невалидный HTML. Браузер исправит, но не гарантирует работу. - Использовать
document.write()внутри скрипта. Это убивает производительность и не работает в модулях. - Загружать скрипты через
innerHTMLили динамически вставлять<script>безasyncилиdefer. Это блокирует рендеринг, даже если вы думаете, что «это же позже».
Если вы видите код вроде:
<div id="app"></div>
<script>
document.write('<script src="lib.js"></script>');
</script>
- это устаревший подход. Замените его на import или defer.
Практический чек-лист на 2026 год
Вот что делать, когда вы добавляете скрипт:
- Определите тип скрипта: ваш код или сторонний?
- Если ваш код: размещайте в конце
<body>с атрибутомdefer. - Если сторонний (аналитика, реклама, чат-бот): используйте
asyncи ставьте в<head>. - Если это полифилл: ставьте в
<head>сdefer. - Если это модуль: ставьте в
<body>-deferвключён автоматически. - Никогда не используйте
document.write()или динамическую вставку скриптов без атрибутов.
Как проверить, что вы всё сделали правильно?
Откройте DevTools в браузере (F12), перейдите на вкладку Network.
- Смотрите, когда загружаются ваши скрипты - до или после рендера контента.
- Если скрипт появляется в списке до
index.html- он блокирует загрузку. - Если скрипт загружается параллельно с HTML и выполняется после
DOMContentLoaded- всё правильно.
Также проверьте в Performance - есть ли «parser-blocking» скрипты? Если да - вы разместили их неправильно.
В Google PageSpeed Insights ваш сайт должен получать «Оптимизировано» по пункту «Устранение ресурсов, блокирующих рендеринг».
Что будет, если игнорировать это?
Если вы ставите скрипт в <head> без defer или async:
- Пользователь видит пустой экран 1-3 секунды - даже если контент уже загружен.
- Сайт теряет позиции в поиске - Google учитывает скорость загрузки.
- Повышается отказ - люди уходят, не дождавшись кнопки.
- Вы теряете конверсии - даже если ваш продукт отличный.
Это не «маленькая деталь». Это фундаментальная часть UX.
Правильное размещение скрипта - это не про «как написать код». Это про то, чтобы пользователь увидел ваш сайт, а не пустой экран.
Можно ли ставить скрипт в <head> без атрибутов?
Можно, но это блокирует рендеринг страницы. Браузер останавливает загрузку HTML, пока скрипт не скачается и не выполнится. Это приводит к задержке отображения контента. Используйте только если скрипт критичен для начального рендера (например, полифилл для темной темы) и обязательно добавьте атрибут defer.
Чем отличается async от defer?
async скачивает скрипт параллельно и запускает его сразу после загрузки - без ожидания других скриптов. Это подходит для независимых скриптов, таких как аналитика. defer тоже загружает параллельно, но запускает только после полного разбора HTML и в порядке их следования в коде. Это безопаснее для вашего собственного JavaScript.
Нужно ли ставить defer для модулей?
Нет, не нужно. Атрибут defer включён автоматически для скриптов с type="module". Вы не можете его отключить. Это сделано специально, чтобы модули не ломали порядок выполнения и не блокировали рендер.
Что делать, если скрипт использует document.getElementById()?
Если скрипт находится в <head> - он может не найти элементы, потому что DOM ещё не сформирован. Решение: перенесите скрипт в конец <body> или оберните код в событие DOMContentLoaded: document.addEventListener('DOMContentLoaded', () => { /* ваш код */ });.
Можно ли использовать несколько скриптов в <body>?
Да, можно. Но если они зависят друг от друга - например, сначала загружается jQuery, потом ваш скрипт - используйте defer для обоих. Он гарантирует, что они выполнятся в порядке, в котором вы их написали. Без defer - порядок не гарантирован.
Почему Google рекомендует ставить скрипты в конец <body>?
Потому что это ускоряет отображение контента. Пользователь видит текст, картинки, кнопки - даже если скрипты ещё грузятся. Это улучшает восприятие скорости, снижает отказы и повышает SEO-позиции. Это не просто рекомендация - это стандарт современной веб-разработки.
Правильное размещение скрипта - это не про «как сделать, чтобы заработало». Это про то, чтобы пользователь не ушёл, пока он грузится. В 2026 году - это уже не опция. Это базовое требование.