Step 1 — Creating Test Files
In this step, we will create several test files in the default Nginx directory. We’ll use these files later to check Nginx’s default behavior for ’s compression and test that the configuration changes have the intended effect.
To infer what kind of file is served over the network, Nginx does not analyze the file contents; that would be prohibitively slow. Instead, it looks up the file extension to determine the file’s MIME type, which denotes its purpose.
Because of this behavior, the content of our test files is irrelevant. By naming the files appropriately, we can trick Nginx into thinking that, for example, one entirely empty file is an image and another is a stylesheet.
Create a file named in the default Nginx directory using . This extension denotes that it’s an HTML page:
Let’s create a few more test files in the same manner: one image file, one stylesheet, and one JavaScript file:
The next step is to check how Nginx behaves with respect to compressing requested files on a fresh installation with the files we have just created.
Http заголовки для управления клиентским кэшированием
Для начала давайте посмотрим, как сервер и браузер взаимодействуют при отсутствии какого-либо кэширования. Для наглядного понимания я попытался представить и визуализировать процесс общения между ними в виде текстового чата. Представьте на несколько минут, что сервер и браузер – это люди, которые переписываются друг с другом
Без кэша (при отсутствии кэширующих http-заголовков)
Как мы видим, каждый раз при отображении картинки cat.png браузер будет снова загружать ее с сервера. Думаю, не нужно объяснять, что это медленно и неэффективно.
Заголовок ответа и заголовок запроса .
Идея заключается в том, что сервер добавляет заголовок к файлу (ответу), который он отдает браузеру.
Теперь браузер знает, что файл был создан (или изменен) 1 декабря 2014. В следующий раз, когда браузеру понадобится тот же файл, он отправит запрос с заголовком .
Если файл не изменялся, сервер отправляет браузеру пустой ответ со статусом . В этом случае, браузер знает, что файл не обновлялся и может отобразить копию, которую он сохранил в прошлый раз.
Таким образом, используя мы экономим на загрузке большого файла, отделываясь пустым быстрым ответом от сервера.
Заголовок ответа и заголовок запроса .
Принцип работы очень схож с , но, в отличии от него, не привязан ко времени. Время – вещь относительная.
Идея заключается в том, что при создании и каждом изменении сервер помечает файл особой меткой, называемой , а также добавляет заголовок к файлу (ответу), который он отдает браузеру:
Теперь браузер знает, что файл актуальной версии имеет равный “686897696a7c876b7e”. В следующий раз, когда брузеру понадобится тот же файл, он отправит запрос с заголовком .
Сервер может сравнить метки и, в случае, если файл не изменялся, отправить браузеру пустой ответ со статусом . Как и в случае с браузер выяснит, что файл не обновлялся и сможет отобразить копию из кэша.
Заголовок
Принцип работы этого заголовка отличается от вышеописанных и . При помощи определяется “срок годности” (“срок акуальности”) файла. Т.е. при первой загрузке сервер дает браузеру знать, что он не планирует изменять файл до наступления даты, указанной в :
В следующий раз браузер, зная, что “дата истечения срока годности” еще не наступила, даже не будет пытаться делать запрос к серверу и отобразит файл из кэша.
Такой вид кэша особенно актуален для иллюстраций к статьям, иконкам, фавиконкам, некоторых css и js файлов и тп.
Заголовок с директивой .
Принцип работы очень схож с . Здесь тоже определяется “срок годности” файла, но он задается в секундах и не привязан к конкретному времени, что намного удобнее в большинстве случаев.
Для справки:
- 1 день = 86400 секунд
- 1 неделя = 604800 секунд
- 1 месяц = 2629000 секунд
- 1 год = 31536000 секунд
К примеру:
У заголовка , кроме , есть и другие директивы. Давайте коротко рассмотрим наиболее популярные:
public
Дело в том, что кэшировать запросы может не только конечный клиент пользователя (браузер), но и различные промежуточные прокси, CDN-сети и тп. Так вот, директива позволяет абсолютно любым прокси-серверам осуществлять кэширование наравне с браузером.
private
Директива говорит о том, что данный файл (ответ сервера) является специфическим для конечного пользователя и не должен кэшироваться различными промежуточными прокси. При этом она разрешает кэширование конечному клиенту (браузеру пользователя). К примеру, это актуально для внутренних страниц профиля пользователя, запросов внутри сессии и т.п.
no-cache
Позволяет указать, что клиент должен делать запрос на сервер каждый раз. Иногда используется с заголовком , описанным выше.
no-store
Указывает клиенту, что он не должен сохранять копию запроса или частей запроса при любых условиях. Это самый строгий заголовок, отменяющий любые кэши. Он был придуман специально для работы с конфиденциальной информацией.
must-revalidate
Эта директива предписывает браузеру делать обязательный запрос на сервер для ре-валидации контента (например, если вы используете eTag). Дело в том, что http в определенной конфигурации позволяет кэшу хранить контент, который уже устарел. обязывает браузер при любых условиях делать проверку свежести контента путем запроса к серверу.
proxy-revalidate
Это то же, что и , но касается только кэширующих прокси серверов.
s-maxage
Практически не отличается от , за исключением того, что эта директива учитывается только кэшем резличных прокси, но не самим браузером пользователя. Буква “s-” исходит из слова “shared” (например, CDN). Эта директива предназначена специально для CDN-ов и других посреднических кэшей. Ее указание отменяет значения директивы и заголовка . Впрочем, если вы не строите CDN-сети, то вам вряд ли когда-либо понадобится.
Reference 1. $upstream_cache_status values:
- – The response was not found in the cache and so was fetched from an origin server. The response might then have been cached.
- – The response was fetched from the origin server instead of served from the cache because the request matched a directive (see below.) The response might then have been cached.
- – The entry in the cache has expired. The response contains fresh content from the origin server.
- – The content is stale because the origin server is not responding correctly, and was configured.
- – The content is stale because the entry is currently being updated in response to a previous request, and is configured.
- – The directive was enabled and NGINX verified that the current cached content was still valid ( or ).
- – The response contains valid, fresh content direct from the cache.
Reference 2. Memcache tools
You may install nice tools pack: “libmemcached-tools”.
Nice examples on usage:
memcdump — servers=localhostmemccat — servers=localhost memcdump — servers=localhost | less
1: Создание тестовых файлов
На начальном этапе мы создадим несколько тестовых файлов в стандартном каталоге Nginx. Мы будем использовать эти файлы позже, чтобы проверить поведение Nginx при сжатии gzip и узнать, имеет ли сжатие желаемый эффект.
Чтобы определить, какой тип файла обслуживается по сети, Nginx не анализирует все его содержимое – это было бы слишком долго. Вместо этого он смотрит на расширение и определяет MIME-тип файла, который определяет его цель.
Это значит, что содержимое наших тестовых файлов не имеет значения. Называя файлы соответствующим образом, мы можем обмануть Nginx, заставить его думать, что, например, один пустой файл является изображением, а другой – таблицей стилей.
Создайте файл test.html в каталоге Nginx, используя truncate. Расширение этого файла означает, что это HTML-страница:
Давайте создадим еще несколько тестовых файлов с другими расширениями – jpg, css и js.
Следующим шагом будет проверка того, как Nginx ведет себя при обработке запрошенных файлов (на примере тех файлов, что мы только что создали).
Оптимизация работы соединений
Настроим несколько основных параметров, которые отвечают за количество обрабатываемых соединений и сроки их поддержания.
— Определяет количество рабочих процессов. Обычно, выставляют равному числу ядер, но в новых версиях его лучше устанавливать в auto. По умолчанию 1
— Устанавливает максимальное количество соединений одного рабочего процесса, то есть nginx будет обрабатывать * , остальные запросы ставить в очередь. Следует выбирать значения от 1024 до 4096. По умолчанию 512.
— Позволяет принимать максимально возможное количество соединений. Иначе, процесс nginx за один раз будет принимать только одно новое соединение. По умолчанию off.
— Отвечает за максимальное время поддержания keepalive-соединения, в случае, если пользователь по нему ничего не запрашивает. Для современных систем, стоит выставить от 30 до 50. В нашем случае 45. По умолчанию 75.
— Если клиент перестал читать страницу, Nginx будет сбрасывать соединение с ним. По умолчанию off.
— Ждет выставленное количество секунд тело запроса от клиента, после чего сбрасывает соединение. По умолчанию 60.
— Если клиент прекратит чтение ответа, Nginx подождет выставленное количество секунд и сбросит соединение. По умолчанию 60.
Как уменьшить размер HTML
Для уменьшения размера HTML-страницы нужно сжать код и облегчить элементы:
- Избавиться от переадресации с целевой страницы. Google пишет о том, что перенаправления типа example.com → www.example.com → m.example.com или example.com → m.example.com/home для мобильных пользователей замедляют загрузку страницы.
- Оформить HTML-элементы с помощью CSS, это ускорит загрузку и упростит работу с повторяющимися на страницах элементами.
- Сжать все текстовые файлы HTML, XML, CSS, Javascript, сжать HTML-код страниц.
- Использовать минификацию — удалить ненужные данные, которые увеличивают объем кода.
- Сжать все графические файлы, оптимизировать изображения — фотографии и графику.
- Использовать кэш браузера — кэшировать данные в браузере пользователя.
- Оптимизировать нефункциональные анимационные детали, отказаться от flash — такие элементы вредят безопасности сайта и могут не поддерживаться у пользователей.
- Оптимизировать количество рекламных блоков на странице.
2: Проверка стандартного поведения Nginx
Давайте проверим как обрабатывается HTML-файл test.html – со сжатием или без. Команда запрашивает файл с сервера Nginx и через HTTP-заголовок (Accept-Encoding: gzip) указывает, что можно использовать сжатый с помощью gzip контент:
В ответ вы должны увидеть несколько заголовков HTTP-ответа:
В последней строке вы можете увидеть заголовок Content-Encoding: gzip. Это говорит нам о том, что для отправки этого файла использовалось сжатие. Произошло это потому, что в Nginx сжатие включено автоматически – то есть даже в новой установке Ubuntu.
Однако по умолчанию Nginx сжимает только файлы HTML. Все остальные файлы будут обслуживаться без сжатия, что не совсем хорошо. Чтобы убедиться в этом, вы можете запросить тестовый файл test.jpg:
Результат будет немного отличаться от предыдущего:
В выводе заголовок Content-Encoding: gzip отсутствует, а это означает, что файл был обработан без сжатия.
Вы можете повторить тест с таблицей стилей CSS:
И снова в выводе не будет упоминания о сжатии:
Давайте настроим Nginx так, чтобы он мог сжимать все типы файлов, для которых полезно использовать gzip.
Rule out the session-ecosystem
If you want to use nginx caching with rails you must prevent session cookie from being set, since ignoring Set-Cookie at nginx levels and caching response when Set-Cookie being used is THE WORST idea.
Preventing session from being set you will also brake forgery protection, flash logic and devise gem in your app, so you cannot ignore session globally, but you can ignore them using before_action or after_action callback with any condition you need.
after_action skip_session_for_public_cachedef skip_session_for_public_cache if !keep_session? request.session_options = true#if you using cookie_store you need to delete session cookie! cookies.delete('_rails_app_session') endend
Upd: Full case and workarounds for session ecosystem I strongly recommend you to read here.
What are HTTP headers?
HTTP headers are the core part of HTTP requests and responses and provide required information about the request or response. All the headers are case-insensitive, headers fields are separated by colon, key-value pairs in clear-text string format. These headers contain information about each communication. For example, the request header contains, information on what resource is being requested, which browser the client is using and what data formats the client will accept. While response headers contain information on, whether the request was successfully fulfilled and the language and format of any resources in the body of the response.
The cache-control header is broken up into directives. You can see the cache-control header of https://google.com with the following command:
curl -I https://google.com
You should get the following output:
HTTP/1.1 301 Moved Permanently Location: https://www.google.com/ Content-Type: text/html; charset=utf-8 Date: Fri, 05 Jun 2020 03:03:10 GMT Expires: Sun, 05 Jul 2020 03:03:10 GMT Cache-Control: public, max-age=2592000 Server: gws Content-Length: 220 X-XSS-Protection: 0 X-Frame-Options: SAMEORIGIN Alt-Svc: h3-27=":443"; ma=2592000,h3-25=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
As you can see, the part to the left of the colon is cache-control and the value is on the right of the colon, and there can be one or several comma-separated values for cache control. These values are called directives, and they dictate who can cache a resource as well as how long those resources can be cached before they must be updated.
The most common cache-control headers are detailed below:
Cache-Control: Public
This directive indicates that the response may be stored by any cache, even if the response is normally non-cacheable.
Cache-Control: Private
This directive indicates that the response can only be cached by the browser that is accessing the file. It can not be cached by an intermediary agent such as proxy or CDN.
Cache-Control: Max-Age
This directive indicates that the maximum amount of time a resource is considered fresh. In other words how many seconds a resource can be served from cache after it’s been downloaded. For example, if the max age is set to 3600 means that the returned resource is valid for 3600 seconds, after which the browser has to request a newer version.
You can also use a technique developed by some assets builders tools, like Webpack or Gulp to force the browser to download a new version of the needed file. This will precompiled each file on the server and add hash sums to the file names, such as “app-72420c47cc.css”. So, after next the deployment, you will get a new version of the file.
Cache-Control: No-Cache
This directive indicates that a browser may cache a response, but must first submit a validation request to an origin server. This directive is not effective in preventing caches from storing your response. It allows you to cache but subsequence response or any subsequence response for similar data the client needs to check with the browser whether that resource has changed or not. Only if the resource has not changed then the client serves the cache which is stored.
If you apply the technique you learned in the previous section in html files, you will never get new links for your css, js, or image files until you force a reload.
It is recommended to use Cache-Control: no-cache to html files to validate resources on the server before use it from the cache.
Cache-Control: No-Store
This directive indicates that the response should never be cached, For example, banking details you would not want to be stored in any browser cache. For those kinds of purposes, you can use no-store.
Зачем уменьшать размер HTML-страницы
Большой вес страницы — одна из причин медленной загрузки, поэтому рекомендуем сжимать объекты и избавляться от ненужных элементов. Это не единственная причина медленной загрузки ресурса, на нее влияет много факторов, но всегда лучше исправить то, что доступно.
Пользователи не будут ждать долгой загрузки, максимум, который они ждут — 2-3 секунды на десктопе или 3-4 на мобильном устройстве. Если сайт так и не загрузился, пользователь закроет страницу — для поисковиков это будет значить, что сайт не удовлетворяет задачи пользователей. Поисковики стимулируют веб-мастеров ускорять и облегчать сайты. Обновление
Google Speed Update занижает позиции очень медленных сайтов, к тому же Google переводит сайты в Mobile-first index — это значит, что mobile-friendly сайты получат преимущество, десктопная выдача будет строиться на основе мобильной, где особенно важен вес страницы.
Иногда незначительные задержки скорости не критичны, если посетители целенаправленно хотят получить услуги, товары или информацию с конкретного сайта. К примеру, по данным инструмента
Google PageSpeed Insights, у сайта amazon.com довольно низкая скорость загрузки с мобильных устройств, но Amazon востребован: пользователи готовы ждать, чтобы делать выгодные заказы.
Анализ amazon.com
Скорее всего Amazon рассчитывает на то, что пользователи со смартфонов будут скачивать мобильное приложение, поэтому не работает над скоростью мобильной версии сайта.
Измерить скорость загрузки своего сайта и сравнить с конкурентными можно с помощью инструментов
Google PageSpeed Insights или Проверка скорости сайта от PR-CY.
Фрагмент результатов проверки
Если хотите ускорить загрузку страницы, то рекомендуем уменьшить ее размер.
Сжать фотографии, иллюстрации и другую графику
На сайтах часто используют много больших графических элементов в хорошем качестве — фотографии в высоком разрешении, баннеры, иллюстрации. Если они много весят, то тормозят загрузку страницы, поэтому их нужно сжать
Даже если для просмотра принципиально важно хорошее качество и большой размер фотографий, есть способы сжимать изображения без визуальных потерь.
Как оптимизировать картинки для сайта:
-
Подберите разрешение.
Незачем загружать изображение в большом разрешении, если оно будет отображаться в маленьком без увеличения по клику. -
Подберите формат.
JPEG подходит для фотографий, PNG для дизайнерской графики, SVG для вектора. Google также индексирует формат WebP, который весит меньше, но не все браузеры его поддерживают. Яндекс не индексирует SVG и изображения в скриптах. -
Уменьшайте количество цветов.
Изображения, где нет сложных градиентов, требуют меньшего количества цветов. Можно оптимизировать картинку без потери качества, выбрав палитру меньше, тогда изображение будет хранить меньшее количество битов на пиксель.Слева направо: 32 бита (16M цветов), 7 бит (128 цветов), 5 бит (32 цвета)
Пропишите параметры в CSS.
Укажите размеры в коде или в редакторе изображений CMS. Для разных экранов и дисплеев с матрицей Retina нужны дополнительные варианты изображения разных размеров, чтобы браузер загружал нужное для устройства.
Используйте шрифты.
Если вы еще используете графику вместо шрифтов для текста, замените надписи на шрифты, это удобнее и меньше весит. Такой текст можно скопировать, поменять, масштабировать в любой момент.
Удалите лишние изображения.
Неинформативные картинки, иллюстрации ради разбивки текста и непонятные схемы лучше заменить на качественные изображения, которые помогут понять тему материала, или вообще удалить, чтобы они не прибавляли вес странице.
Минифицируйте.
Удаляйте XML-разметку с лишними метаданными, она появляется при работе с картинками в некоторых графических приложениях. EXIF — информацию о геоданных, дате съемки, фотокамере тоже можно удалить.
Используйте алгоритмы сжатия.
Настройте на сервере gzip-сжатие для SVG-графики.
Инструменты и сервисы для оптимизации изображений на сайте:
- CompressJPEGСервис для сжатия JPEG и PNG без потерь качества.
-
PunyPNG
Инструмент сжимает PNG, JPEG и GIF. - TinyPNGИнструмент для оптимизации изображений в PNG и JPEG.
- JpegtranИнструмент для оптимизации JPEG-изображений.
- OptipngИнструмент для оптимизации PNG без потерь.
- PngquantИнструмент сжимает PNG-изображения.
Вес HTML-страницы — один из факторов влияния на скорость ее загрузки у пользователей, поэтому ускорить загрузку можно, если облегчить вес страницы. Для этого рекомендуем сжать и сократить код, сжать изображения и избавиться от лишних элементов — неинформативных картинок, тяжелых анимаций и флеша.
Step 3 — Configuring Nginx’s gzip Settings
To change the Nginx configuration, open the main Nginx configuration file in or your favorite text editor:
Find the settings section, which looks like this:
/etc/nginx/nginx.conf
You can see that compression is indeed enabled by the directive, but several additional settings are commented out with sign and have no effect. We’ll make several changes to this section:
- Enable the additional settings by uncommenting all of the commented lines (i.e., by deleting the at the beginning of the line)
- Add the directive, which tells Nginx not to compress files smaller than 256 bytes. Very small files barely benefit from compression.
- Append the directive with additional file types denoting web fonts, icons, XML feeds, JSON structured data, and SVG images.
After these changes have been applied, the settings section should look like this:
/etc/nginx/nginx.conf
Save and close the file to exit.
To enable the new configuration, restart Nginx:
Next, let’s make sure our new configuration works.
Never use Expires_in.
Why you should never use inside rails + Nginx stack? In two words it makes very hard to bypass all caches and involve application logic .
Lets review an example:
before_action :cache_with_expiration, only: def cache_with_expiration expires_in( 1.minute, public: true )end
What do we got here? First of all we got . Now both Rack::Cache and Nginx will cache this response for a minute.
What you should do to bypass them and involve rails logic:
- First of all you’ll need to hide in nginx with directive, otherwise browser will serve page from cache for a minute.
- In Rack::Cache config you must set allow_reload or allow_revalidate to true, or Rack::Cache will serve its copy of content.
- Inside nginx we must add with desired condition
- If request doesn’t contain Cache-Control wich can go through allow_reload or allow_revalidate you must use to add missing value ( either , or ).
- You will need to purge Rack::Cache when deploying new version of application, this is not easily done, see “Rack::Cache purge” section.
Keeping expiration cache in two places is a baaad idea. Nginx cache in my opinion is much better place for it, than Rack.
Step 2 — Checking the Default Behavior
Let’s check if the HTML file named is served with compression. The command requests a file from our Nginx server and specifies that it is fine to serve compressed content by using an HTTP header ():
In response, you should see several HTTP response headers:
In the last line, you can see the header. This tells us that compression was used to send this file. That’s because Nginx has compression enabled automatically even on the fresh Ubuntu 20.04 installation.
However, by default, Nginx compresses only HTML files. Every other file will be served uncompressed, which is less than optimal. To verify that, you can request our test image named in the same way:
The result should be slightly different than before:
There is no header in the output, which means the file was served without any compression.
You can repeat the test with the test CSS stylesheet:
Once again, there is no mention of compression in the output:
In the next step, we’ll tell Nginx to compress all sorts of files that will benefit from using .
Configure Cache-Contol Headers for Apache and Nginx Webserver
In this section, we will show you how to set the HTTP Cache-Control header in Apache and Nginx.
Apache
For the Apache web server, you will need edit your website virtual host configuration file in order to implement the HTTP Cache-Control header, for example:
nano /etc/apache2/sites-enabled/webdock.conf
Add the following contents:
<filesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$"> Header set Cache-Control "max-age=3600, public" </filesMatch>
If you want to enable Cache-Control for all files, add Header set line outside the filesMatch block.
As you can see, we set the Cache-Control header’s max-age to 3600 seconds and to public for the listed files.
This can also be set in a .htaccess file in any directory below your web root. If you are using SSL you should consider adding this to the ssl version of your virtual host configuration, which is also placed in /etc/apache2/sites-enabled/
Nginx
For the Nginx web server, you will need to edit your website virtual host configuration file to implement the HTTP Cache-Control header, for example:
nano /etc/nginx/sites-enabled/webdock
Add the following contents:
location ~* \.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$ { expires 1h; add_header Cache-Control "public, no-transform"; }
If you want to enable Cache-Control for all files, add a add_header line without the enclosing location block, as what the location block does is specify specific filetypes you are targeting with your directives (ico,pdf,flv etc.).