Продолжаем цикл публикаций о статических сайтах на базе нашего облачного хранилища (см. предыдущие публикации здесь и здесь). Сегодня мы подробно обсудим вопросы их тонкой настройки и оптимизации.
Главным критерием отличной работы сайта с точки зрения пользователя является, конечно же, скорость загрузки компонентов. Если сайт по тем или иным причинам загружается слишком долго, это неизбежно приводит к потере посетителей, которым надоедает ждать. Чтобы сделать сайт быстрым и удобным, нужно проделать определенную работу по его оптимизации.
Ниже мы дадим ряд рекомендаций, с помощью которых можно увеличить скорость работы статического сайта, размещенного в нашем хранилище.
Совет 1. Используйте возможности CDN
О подключении к нашему облачному хранилищу CDN от компании Akamai мы уже писали. CDN хранит весь статичный контент (изображения, текстовые файлы, JS, CSS и так далее) на кэширующих серверах, разбросанных по всему миру (см. карту здесь).
При обращении к веб-странице или ее ресурсам запрос будет обработан географически ближайшим к клиенту кэширующим сервером. Использование CDN помогает увеличить скорость загрузки сайта как для стационарных, так и для мобильных устройств.
По умолчанию все данные кэшируются в CDN на 24 часа. Недавно в хранилище была добавлена новая функция, с помощью которой можно очистить кэш CDN в любой момент:
Для этого достаточно перейти на показанную на рисунке вкладку и ввести в форму адреса страниц, кэш которых требуется очистить. Кэш будет очищен не моментально, а примерно через 15 минут после отправки формы.
Совет 2. Не забывайте о настройках кэширования
Любая веб-страница включает множество разных элементов: изображения, скрипты, файлы стилей и так далее. Пользователь, посещающий страницу в первый раз, получает все эти элементы, выполнив ряд HTTP-запросов. Чтобы избежать повторной загрузки большого количества файлов, используется кэширование.
В основе модели кэширования, используемой в протоколе HTTP, лежат так называемые валидаторы — специальные заголовки, используемые клиентом для того, чтобы убедиться, что кэшируемый документ все еще актуален. Благодаря валидаторам клиент может проверять состояние документа, не передавая на сервер кэшированную копию целиком. В свою очередь сервер передает в ответе документ только в том случае, если полученный им валидатор свидетельствует о наличии в кэше клиента устаревшей копии.
Валидаторы подразделяются на сильные и слабые. Сильные валидаторы появились в HTTP/1.1. Они называются так потому, что они изменяются всякий раз, когда изменяется файл. К ним относятся так называемые ETags (entity tags). ETag представляет собой идентификатор содержимого документа; он изменяется, если в документе изменится хотя бы один бит. В качестве идентификатора может использоваться, например, MD5-cумма содержимого документа. Когда клиент запрашивает с сервера документ, значение ETag передается в ответе, например:
HTTP/1.1 200 OK Server: Selectel_Storage/1.0 Accept-Ranges: bytes Last-Modified: Mon, 18 Aug 2014 12:25:38 GMT X-Timestamp: 1408364738.80296 Content-Type: image/jpeg Content-Length: 458073 Access-Control-Allow-Origin: * Access-Control-Expose-Headers: Last-Modified, ETag, X-Timestamp ETag: "ebef3343a7b152ea7302eef75bea46c3" Date: Wed, 20 Aug 2014 11:52:48 GMT
При повторном запросе этого же документа сохраненное значение валидатора передается уже в заголовке If-None-Match:
GET / HTTP/1.1 Host: example.org If-None-Match:"ebef3343a7b152ea7302eef75bea46c3"
Если документ не был изменен, то в ответе сервер вернет только заголовки и код 304 Not Modified. Иначе сервер вернет код 200 и передаст новую версию документа, а также новое значение ETag для нее.
В нашем хранилище и ETag генерируется сразу же после загрузки файла. Он представляет собой MD5-хэш содержимого. Если контент изменяется, то изменяется и ETag.
Слабыми называются валидаторы, которые не обязательно изменяются при каждом изменении файла.
Примером слабого валидатора может служить заголовок Last-Modified. Значением этого заголовка является дата последнего изменения файла. В нашем хранилище оно устанавливается автоматически. Если указать при запросе в заголовке If-Modified-Since дату не ранее, той, которая в данный момент содержится в заголовке Last-Modified, то ответом также будет являться 304 Not Modified.
Сильные валидаторы могут использоваться в любом контексте. Слабые валидаторы используются в контексте, который не зависит от точного содержания файла.
Например, валидаторы обоих типов могут быть использованы в GET-запросах с условием (If Modified Since или If None Match). Однако при загрузке файлов по частям могут использоваться только сильные валидаторы — иначе клиент получит файл в неконсистентном виде.
Совет 3. Обратите внимание на заголовок Cache-Control
Чтобы устанавливать срок хранения в кэше браузера копии файла, оригинал которого находится в хранилище, используется заголовок Cache-Control с директивой max-age. Благодаря этому заголовку можно достаточно сильно увеличить скорость загрузки сайта — если файл закэширован, то браузер будет мгновенно отображать контент из кэша, не делая ни одного запроса к сайту.
Время хранения файла в кэше указывается в секундах:
Cache-Control: max-age=7200
В приведенном примере оно составляет 7200 секунд (2 часа). Обычно таким способом кэшируются CSS, JS и графические файлы. Их желательно кэшировать навсегда, а при изменении содержимого изменять ссылки на них в HTML. В RFC 2616 для таких файлов рекомендуется указывать время кэширования, не превышающее 1 год:
Cache-Control: max-age=31536000
Если требуется, чтобы определенный файл не кэшировался, а всегда отдавался «свежим», для заголовка Cache-Control устанавливается следующее значение:
Cache-Control: no-cache
Оно указывает, что элемент вообще не должен кэшироваться и что клиент должен запрашивать его при каждом обращении к хранилищу (время загрузки файла в этом случае увеличится, так как придется скачать тело файла).
Еще один способ, с помощью которого можно всегда получать файл в актуальной версии, заключается в добавлении к имени файла контрольной суммы содержимого.
Если содержимое файла изменится хотя бы на один бит, то и контрольная сумма тоже изменится. Если никаких изменений не было, то браузер использует файл из кэша. При изменении файла изменится ссылка на него, и будет загружена новая версия.
Получить контрольную сумму можно как с помощью стандартных утилит md5sum или sha1sum, так и с помощью специальных утилит.
Можно также добавлять к ссылкам на файлы произвольный набор символов — например, метку времени (http://example.com/script.js?timestamp_here),— и обновлять ссылки при каждом деплое сайта. При использовании такого способа, однако, нет никакой гарантии того, что браузер не будет делать лишних запросов: даже на файлы, содержимое которых не изменялось, будет вести уже другая ссылка (ключом кэширования выступает вся ссылка целиком вместе с query-параметрами), и придется скачивать их заново.
Для HTML-страниц предпочтительнее устанавливать для заголовка Сache-Сontrol значение no-cache. Если нужно что-то срочно изменить на странице, а у клиента эта страница уже закэширована (современные браузеры делают это по умолчанию), то клиент может вообще не увидеть внесенных изменений.
Это особенно важно при использовании CDN: СDN Akamai по умолчанию кэширует файлы без соответствующих заголовков на 24 часа. Можно, конечно, очистить кэш (см. выше), но придется ещё ждать как минимум 15 минут после отправки соответствующего запроса. Установка же значения no-cache поможет избежать возможных проблем — страница всегда будет загружаться в актуальном виде. Браузеры в этом случае все равно будут использовать заголовки If-None-Match (или If-Modified-Since), и страница, которая не была изменена, не будет загружаться лишний раз.
В некоторых случаях время кэширования HTML-страниц лучше указывать исходя из частоты изменений. Например, если страница с новостями на сайте обновляется каждый час, то для max-age можно установить значение 3600 (1 час).
Значение заголовка Cache-Control (равно как и другиx HTTP-заголовков) в нашем хранилище можно установить через веб-интерфейс:
Через веб-интерфейс устанавливаются значения заголовков только для контейнера в целом. Значения заголовков для отдельных файлов можно установить только через API или с помощью сторонних клиентов.
Вместо Cache-Control можно использовать заголовок Expires. В его значении указывается дата в формате RFC 1123 date format, по наступлении которой файл перестает быть актуальным (например: Tue, 31 Jan 2012 15:02:53 GMT). До наступления этой даты браузер не будет делать запросов к сайту, а будет получать файл из кэша. После этой даты файл будет загружен снова.
Совет 4. Используйте сжатие gzip
C помощью сжатия можно существенно ускорить загрузку сайта. Начиная с HTTP/1.1 клиенты сообщают о поддерживаемых методах сжатия в заголовке Accept-Encoding:
Accept-Encoding: gzip, deflate
В ответе сервера информация об используемом методе сжатия передается в заголовке Content-Encoding:
Content-Encoding: gzip
Одним из самых популярных и наиболее часто используемых сегодня методов является, конечно же, gzip. C его помощью можно существенно сократить время загрузки. Gzip особенно эффективно работает с текстовыми файлами: HTML, CSS, JS. Благодаря сжатию размер текстовых файлов (и, соответственно, объем передаваемого трафика) уменьшается в среднем в 5-10 раз. Это позволяет значительно увеличить скорость загрузки страницы, что особенно актуально для мобильных клиентов с медленным соединением.
Для графических файлов использовать gzip смысла нет: сжатие не помогает значительно снизить их размер, а часто даже увеличивает его.
В Akamai CDN gzip используется для большинства текстовых файлов по умолчанию.
Совет 5. Минифицируйте JS и CSS
Под минификацией понимается удаление лишних/необязательных символов из файла с целью уменьшения его размера и сокращения времени загрузки. Благодаря этому размер файлов уменьшается в среднем в 1,5—3 раза. Сегодня широкое распространение получает практика минификации не только JS и CSS, но и других типов файлов (HTML, графических файлов и т.д.).
Для минификации используются специальные инструменты, в частности:
- UglifyJS 2
- Google Closure Compiler
- YUI Compressor
- JSMin
- Clean CSS
- CSS Minifier
- html-minifier
- imagemin
С помощью минификации можно не только убирать незначимые пробелы и переносы строк (в CSS и JS они опциональны), но и выполнять более сложные операции. Например, в JS функцию вида:
function summ(first_param, second_param) { return (first_param + second_param); }
Можно превратить в function s(a,b){return(a+b)} и далее везде в коде использовать s вместо summ, при этом полностью сохранив логику её работы. Посмотреть, как работает процедура минификации JavaScript, можно на странице http://lisperator.net/uglifyjs/ в разделе Open Demo.
Совет 6. Используйте конкатенацию
Современные браузеры делают в среднем 6 параллельных запросов на домен. Если сайт содержит много файлов небольшого размера, время его загрузки может затянуться — особенно это заметно при медленном или нестабильном соединении.
Здесь может помочь конкатенация — объединение нескольких файлов одного типа (например, JS или CSS) в один. Она позволяет уменьшить количество запросов и тем самым увеличить скорость загрузки страниц.
Конкатенация может также использоваться для ускорения загрузки изображений. Она может осуществляться двумя способами: с помощью внедрения данных в URL и с помощью спрайтов.
Внедрение данных осуществляется с помощью особого вида URL — data: URI. URI (Universal Resource Identifator) может использоваться как в атрибуте src тэга img, так и в URL фонового изображения в CSS.
Для конвертации изображений в data:URI существуют онлайн-инструменты (см., например, здесь и здесь).
Спрайтом называется коллекция изображений, объединенная в одну картинку. Для формирования сайтов используются различные программные инструменты. С помощью CSS можно обращаться к необходимому участку большого изображения и помещать его в нужное место на сайте.
Спрайты помогают увеличить скорость загрузки, но следует отметить, что работа с ними часто бывает сопряжена с затруднениями. Чтобы внести даже небольшое изменение в спрайт, потребуется вносить сопутствующие изменения и в CSS.
В современных инструментах для сборки проектов на JS (Brunch, Grunt, Gulp и других). процедуры минификации и конкатенации можно автоматизировать. Чтобы при помощи одной команды выполнять все необходимые операции с файлами (включая итоговой деплой на сервер), достаточно создать небольшой конфигурационный файл, описывающий порядок и свойства сборки.
Для желающих узнать больше
Особенности разработки и настройки статических сайтов — тема обширная, и в следующих публикациях мы ее обязательно продолжим. Для тех, кто хочет изучить эту тему глубже как в теории, так и на практике, приводим несколько полезных ссылок:
- https://developers.google.com/speed/docs/best-practices/caching/ — рекомендации по оптимизации кэширования от Google;
- https://developers.google.com/speed/ — набор инструментов для анализа и оптимизации скорости работы веб-страниц от Google. Проверяет сайты в режиме онлайн (достаточно просто ввести адрес в соответствующую форму) и дает рекомендации по оптимизации скорости загрузки файлов. Включает также плагины для Chrome и Firefox, с помощью которых можно диагностировать сайты непосредственно из браузера;
- http://tools.pingdom.com/fpt/ — онлайн-инструмент для измерения скорости загрузки сайта и диагностики проблем;
- https://developer.yahoo.com/performance/rules.html — рекомендации по увеличению скорости работы веб-сайтов от компании Yahoo!
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html — глава RFC2616 (в нем описывается работа протокола HTTP), посвященная настройкам кэширования.
На все вопросы и замечания мы ответим в комментариях. Со своей стороны мы обещаем рассказать об актуальных тенденциях разработки статических сайтов в одной из ближайших публикаций.
Читателей, которые по тем или иным причинам не могут оставлять комментарии здесь, приглашаем в наш блог.