Содержание
ETag
Собственно сам ETag является идентификатором ресурса, запрошенного клиентом (браузером): если изменений нет, то etag не меняется, если изменения были – изменится и его etag.
Пример его работы – проверяем URL:
[simterm]
$ curl -I etag.rtfm.co.ua HTTP/1.1 200 OK Server: nginx/1.10.3 ... ETag: "5ba22fcb-6" Accept-Ranges: bytes
[/simterm]
Теперь добавляем --header If-None-Match
, в котором передаём значение из полученного ETag – и NGINX вернёт нам код 304 Not Modified:
[simterm]
$ curl -IH 'If-None-Match: "5ba22fcb-6"' etag.rtfm.co.ua HTTP/1.1 304 Not Modified Server: nginx/1.10.3 ... ETag: "5ba22fcb-6"
[/simterm]
В таком случае браузер использует закешированное содержимое вместо того, что бы получить всё содержимое с сервера.
Если же ETag будет другим – получим код 200, и всё содержимое index.html
будет передано самим NGINX:
[simterm]
$ curl -IH 'If-None-Match: "blabla"' etag.rtfm.co.ua HTTP/1.1 200 OK Server: nginx/1.10.3 ... ETag: "5ba22fcb-6" Accept-Ranges: bytes
Если же файл будет изменён:
[simterm]
root@ip-172-31-32-251:/etc/nginx# echo index2 > /var/www/etag/index.html
[/simterm]
А в браузере отправить прежнее значение ETag – то NGINX передаст код 200, и всё содержимое файла с новым значением ETag:
[simterm]
$ curl -IH 'If-None-Match: "5ba22fcb-6"' etag.rtfm.co.ua HTTP/1.1 200 OK Server: nginx/1.10.3 ... ETag: "5ba232f9-7" Accept-Ranges: bytes
[/simterm]
gzip
и weak validation
Для ETag имеется два типа валидации контента – strong validation, которая работает по умолчанию, и weak validation.
По ETag и strong/weak валидацию см. тут>>> и тут>>>.
Собственно проблема возникла из-за модификатора W/
, который добавляется NGINX при использовании weak validation – мобильное приложение игнорировало etag и каждый раз выкачивало видео-контент заново.
Выглядит он так:
[simterm]
# curl -I -H "Accept-Encoding: gzip,deflate" etag.rtfm.co.ua/index.html HTTP/1.1 200 OK Server: nginx/1.10.3 ... ETag: W/"5ba367ff-264" Content-Encoding: gzip
[/simterm]
Вот – ETag: W/"5ba367ff-264"
.
А возникает проблема из-за включенного gzip
– заголовок Content-Encoding: gzip
.
На нашем старом production-сервере всё работает, а на новом – кешировение не работает, и как показал “анализ” (grep -r /etc/nginx
, ага) конфигов NGINX – из-за того, что в конфигах виртуалхостов старого сервера gzip
был отключен явно, а на новом – нет.
Собственно, решение – отключить gzip
для виртуалхоста:
server { listen 80; server_name etag.rtfm.co.ua; gzip off; root /var/www/etag; index index.html; }
Проверяем:
[simterm]
# curl -I -H "Accept-Encoding: gzip,deflate" etag.rtfm.co.ua/index.html HTTP/1.1 200 OK Server: nginx/1.10.3 ... ETag: "5ba367ff-264" Accept-Ranges: bytes
[/simterm]
Теперь в ETag нет W/
, и всё работает.
Либо можно его выключить прямо в nginx.conf
(просто убрать gzip on;
), тем более EC2 с NGINX работает за AWS Application Load Balancer, на котором к тому же есть SSL (см. почему не стоит использовать gzip
и SSL – см. тут>>>).
Обсуждение и описание фикса, который добавляет weak validation для gzipped-данных есть тут>>>.
“Bonus”: gzip_min_length
и документация
Если обратить внимание на примеры с curl
в начале поста – то там нет gzip
и W/
в etag. И это было ОК, пока я не дошёл до момента, когда надо было показать пример того, как выглядит weak validation в ответе, и надо было включить gzip
– хотя он включен по умолчанию и, казалось бы, должен был отдаваться сразу.
Но т.к. примеры приводились с тестового хоста, с тестовыми файлами, то возникла интересная проблема: как я ни старался включить gzip
, что бы увидеть его в response headers – он не срабатывал.
Причина оказалась банальна – надо внимательнее читать документацию к модулю, в которой сказано:
Syntax: | gzip_min_length length; |
---|---|
Default: |
gzip_min_length 20; |
Тогда как файл index.html
на тестовом хосте содержал одно слово – “index2“, и был размером:
[simterm]
root@ip-172-31-41-252:/etc/nginx# stat -c %s /var/www/etag/index.html 7
[/simterm]
7 байт – и gzip
его игнорировал.
Решение – явно указать gzip_min_length 0;
в конфиге.