Разработка сайтов и интернет-магазинов Блог веб-разработчика

Ускорение загрузки страницы сайта через htaccess

Изображение сгенерированно ИИ и принадлежит qwen.ai
Изображение сгенерированно ИИ и принадлежит qwen.ai

<p>Недавно проверял скорость одного клиентского проекта. Google PageSpeed ругался на отсутствие базовой оптимизации на стороне сервера. Я открыл файл .htaccess и добавил туда несколько блоков кода. Записал это сюда, чтобы каждый раз не искать синтаксис.</p> <p>❗️Сразу предупреждаю. <strong>Перед любыми правками делайте резервную копию оригинального файла</strong>. Одна опечатка может привести к ошибке 500 и положить весь сайт.</p> <h2>Включаю сжатие данных через mod_deflate</h2> <p>Это мой первый шаг. Если хостинг поддерживает этот модуль, сайт начинает загружаться заметно быстрее. Я добавляю этот блок, чтобы сервер сжимал текстовые файлы перед отправкой в браузер. Браузер получит их в сжатом виде и распакует самостоятельно, что гораздо быстрее, чем качать полный объем данных.</p> <pre><code><span class="hljs-prompt"><IfModule mod_deflate.c></span> <span class="hljs-comment"># Сжимаем HTML, CSS, JS, шрифты, XML, JSON</span> <span class="hljs-constant">AddOutputFilterByType</span> <span class="hljs-constant">DEFLATE</span> text/html text/plain text/xml text/css text/javascript application/javascript application/json application/xml font/ttf font/otf font/woff font/woff2

<span class="hljs-comment"># Не сжимаем уже сжатые форматы</span>
<span class="hljs-constant">SetEnvIfNoCase</span> <span class="hljs-constant">Request_URI</span> \.(?:gif|jpe?g|png|ico|webp|mp4|webm|zip|gz|rar|bz2|tar)<span class="hljs-variable">$ </span>no-gzip dont-vary

<span class="hljs-prompt"></IfModule></span>

</code></pre> <p>Я специально не добавляю сюда изображения, видео или архивы. Они уже сжаты своими форматами. Попытка сжать их на лету только загрузит процессор сервера без реального выигрыша в размере файла.</p> <table> <thead> <tr><th style="text-align:left">Тип файла</th><th style="text-align:left">Зачем сжимать</th><th style="text-align:left">Примерная экономия трафикаю</th></tr> </thead> <tbody> <tr><td style="text-align:left">HTML, XML, JSON</td><td style="text-align:left">Убирает лишние пробелы и переносы строк на уровне передачи</td><td style="text-align:left">60–80%</td></tr> <tr><td style="text-align:left">CSS, JavaScript</td><td style="text-align:left">Основной источник задержек при первой загрузке</td><td style="text-align:left">70–85%</td></tr> <tr><td style="text-align:left">Изображения, видео</td><td style="text-align:left">Уже имеют встроенное сжатие</td><td style="text-align:left">0% (только нагрузка на CPU)</td></tr> </tbody> </table> <h2>Настраиваю кэширование в браузере через mod_expires</h2> <p>Меня всегда раздражало, когда браузер при каждом заходе заново скачивает логотипы, шрифты и скрипты. Чтобы это исправить, я прописываю заголовки Expires. Это прямо говорит браузеру, сколько времени можно хранить файл локально и не обращаться к серверу.</p> <pre><code><span class="hljs-prompt"><IfModule mod_expires.c></span> <span class="hljs-constant">ExpiresActive</span> <span class="hljs-constant">On</span>

<span class="hljs-comment"># Изображения, иконки и шрифты кэшируем на 1 год</span>
<span class="hljs-constant">ExpiresByType</span> image/jpeg <span class="hljs-string">"access plus 1 year"</span>
<span class="hljs-constant">ExpiresByType</span> image/png <span class="hljs-string">"access plus 1 year"</span>
<span class="hljs-constant">ExpiresByType</span> image/webp <span class="hljs-string">"access plus 1 year"</span>
<span class="hljs-constant">ExpiresByType</span> image/svg+xml <span class="hljs-string">"access plus 1 year"</span>
<span class="hljs-constant">ExpiresByType</span> image/x-icon <span class="hljs-string">"access plus 1 year"</span>
<span class="hljs-constant">ExpiresByType</span> font/ttf <span class="hljs-string">"access plus 1 year"</span>
<span class="hljs-constant">ExpiresByType</span> font/woff2 <span class="hljs-string">"access plus 1 year"</span>

<span class="hljs-comment"># CSS и JavaScript кэшируем на 1 месяц</span>
<span class="hljs-constant">ExpiresByType</span> text/css <span class="hljs-string">"access plus 1 month"</span>
<span class="hljs-constant">ExpiresByType</span> application/javascript <span class="hljs-string">"access plus 1 month"</span>

<span class="hljs-comment"># HTML кэшируем ненадолго</span>
<span class="hljs-constant">ExpiresByType</span> text/html <span class="hljs-string">"access plus 1 hour"</span>

<span class="hljs-prompt"></IfModule></span>

</code></pre> <p>Тут важно не переборщить со сроками. Я придерживаюсь простой логики, которая отражена в таблице ниже.</p> <table> <thead> <tr><th style="text-align:left">Тип ресурса</th><th style="text-align:left">Срок хранения в кэше</th><th style="text-align:left">Причина такого выбора</th></tr> </thead> <tbody> <tr><td style="text-align:left">Изображения, шрифты, иконки</td><td style="text-align:left">1 год</td><td style="text-align:left">Эти файлы меняются крайне редко. Нет смысла качать их повторно</td></tr> <tr><td style="text-align:left">CSS и JavaScript</td><td style="text-align:left">1 месяц</td><td style="text-align:left">Обновляются при релизах. Месяц дает баланс между скоростью и актуальностью.</td></tr> <tr><td style="text-align:left">HTML документы</td><td style="text-align:left">1 день (по умолчанию)</td><td style="text-align:left">Чтобы правки в текстах или структуре применялись у пользователей относительно быстро.</td></tr> </tbody> </table> <p>Если поставить кэш на год для HTML, вы потом будете долго сбрасывать кэш браузера, пытаясь понять, почему новые правки в тексте не применились.</p> <h2>Уточняю инструкции через mod_headers</h2> <p>Одного mod_expires иногда недостаточно для современных браузеров и CDN. Я также добавляю заголовок Cache-Control, чтобы дать более жесткие и понятные инструкции сетевым узлам.</p> <pre><code> <span class="hljs-tag"><IfModule mod_headers.c></span> <span class="hljs-tag"><FilesMatch ".(ico|jpe?g|png|gif|webp|svg|woff|woff2|css|js)$"></span> <span class="hljs-keyword"><span class="hljs-common">Header</span></span> set Cache-Control <span class="hljs-string">"public, max-age=2592000, immutable"</span> <span class="hljs-tag"></FilesMatch></span> <span class="hljs-tag"></IfModule></span>

</code></pre> <p>Директива immutable здесь очень полезна. Она сообщает браузеру, что файл точно не изменится в течение указанного времени. Это позволяет браузеру вообще пропускать проверку актуальности файла при обновлении страницы, что экономит лишние HTTP-запросы.</p> <table> <thead> <tr><th style="text-align:left">Директива</th><th style="text-align:left">Что это дает</th></tr> </thead> <tbody> <tr><td style="text-align:left">public</td><td style="text-align:left">Разрешает кэширование не только в браузере пользователя, но и на промежуточных прокси-серверах.</td></tr> <tr><td style="text-align:left">max-age=2592000</td><td style="text-align:left">Устанавливает максимальный возраст кэша в секундах (ровно 30 дней).</td></tr> <tr><td style="text-align:left">immutable</td><td style="text-align:left">Запрещает браузеру делать условные запросы (If-Modified-Since) к этому файлу в течение срока действия кэша.</td></tr> </tbody> </table> <p>Эти настройки дают прирост в баллах Google PageSpeed и делают сайт отзывчивее. Перед тем как копировать код к себе, проверьте в панели хостинга, включены ли модули mod_deflate, mod_expires и mod_headers. Иногда администраторы их отключают, и код просто не сработает. Пишите в комментариях, какие еще правила для скорости вы добавляете в .htaccess по умолчанию.</p>

Теги:

❗️ Код и технические решения в блоге — это исключительно субъективное мнение автора, предоставленное по принципу «как есть» (AS IS) в ознакомительных целях. Не является готовым продуктом или призывом к действию. Автор не гарантирует работоспособность и безопасность кода в вашем окружении. Вы обязаны самостоятельно тестировать решения перед использованием в продакшене. Автор снимает с себя любую ответственность за потерю данных, сбои систем, уязвимости, финансовые убытки или любые другие последствия, возникшие из-за применения материалов блога.

Обсудим вашу задачу и рассчитаем стоимость?

Отвечаю лично в течение пары часов. Никакого спама и автоматических рассылок.