объявление о работе
Дамы и господа, резюме можно отправлять на адрес ivanxjfan@tencent.com.
Пожалуйста, укажите имя резюме в формате: Имя-Должность-Рабочие годы-Место работы (например: Чжан Сан-Внешняя разработка-Пять лет-Чанша.pdf)
Компания:Тенсент
Место:Чанша
Позиция:разработчик веб-интерфейса
Рабочие обязанности:
Отвечает за системные исследования и разработку продуктов Tencent Cloud DNSPod, полные интерфейсные функции системы и реализацию внутреннего логического кода, а также обеспечивает качество продукта и прогресс в исследованиях и разработках.
профессиональные требования:
1. Степень бакалавра или выше, специальность компьютерная, опыт работы более 2 лет;
2. Владение Javascript, html, css и другими технологиями разработки интерфейса с прочной основой;
3. Знакомство с текущими основными интерфейсными фреймворками (react/vue и т. д.), приветствуется опыт разработки на React и Redux;
4. Знакомы с протоколами HTTP и TCP/IP, хорошо разбираетесь в вопросах безопасности и знакомы с общими стратегиями атак и защиты в сети;
5. Хорошие аналитические способности и навыки решения проблем, а также энтузиазм в обучении;
6. Приветствуется опыт разработки Node.js/PHP;
7. Предпочтение отдается разработчикам плагинов с опытом работы в WP или DZ
Примечание. Этот пост подготовлен дочерней компанией Tencent Group. сообщение"
Компания:Тенсент
Место:Штаб-квартира Tencent в Шэньчжэне
Позиция:Старший разработчик веб-интерфейса
Рабочие обязанности:
Отвечает за проектирование системной архитектуры и исследования и разработки продуктов Tencent Cloud Domain Name (DNSPod).
профессиональные требования:
1. Степень бакалавра или выше, специальность компьютерная, опыт работы не менее 5 лет;
2. Владение Javascript, html, css и другими технологиями разработки интерфейса с прочной основой;
3. Знакомство с текущими основными интерфейсными фреймворками (react/vue и т. д.), приветствуется опыт разработки на React и Redux;
4. Знакомы с протоколами HTTP и TCP/IP, хорошо разбираетесь в вопросах безопасности и знакомы с общими стратегиями атак и защиты в области сетевой безопасности;
5. Хорошие аналитические способности и навыки решения проблем, а также энтузиазм в обучении;
6. Приветствуется опыт разработки Node.js/PHP;
предисловие
Я все время читал информацию для своего понимания http, и я забыл следующую статью через несколько дней, просто для себя.http
Весь процесс гребенки, и отhttp
Приходит запрос просто выполнить оптимизацию производительности для прочёсывания.
Основные этапы проектирования следующие:
- Весь процесс http запроса
- Весь процесс разрешения DNS (dns-prefetch, preconnect, preload, prefetch, def, async)
- TCP-соединение (трехстороннее рукопожатие, четырехсторонняя волна (зачем нужно трехстороннее рукопожатие, четырехсторонняя волна))
- Обработка кеша (истечение срока действия, управление кешем (максимальный возраст, общедоступный/частный, без кеша, без хранения, прагма, обязательная повторная проверка), последнее изменение (если-изменить-с момента), etag (если-нет-совпадения) )), кеш браузера (из кеша памяти), сильный кеш, кеш согласования (из кеша диска)
- PageSpend и LightHouse для профилирования
Прочесывание вышеуказанных ссылок основано на результатах анализа предшественников, а все ссылки на статьи будут перечислены позже.
Весь процесс http запроса
Справочная статьяЧто на самом деле происходит, когда вы вводите URL-адрес?
- Введите адрес в адресную строку браузера, например:
fecebook.com
- Браузер ищет IP-адрес по доменному имени (разрешение DNS)
- Браузер отправляет HTTP-запрос на веб-сервер
- Сервер Facebook для постоянного перенаправления
потому что мы вошли
fecebook.com
, вместоhttp://www.facebook.com/
Таким образом, сервер автоматически выполняет постоянное перенаправление и возвращает301код состояния
Почему сервер должен перенаправлять вместо того, чтобы напрямую отправлять содержимое веб-страницы, которую хочет видеть пользователь? На этот вопрос есть много интересных ответов.
Одна из причин связана с ранжированием в поисковых системах. Видите ли, если на странице два адреса, например http://www.igoro.com/ и http://igoro.com/, поисковые системы будут думать, что это два сайта, в результате чего поисковая ссылка для каждого из них будет сокращена, тем самым снижая рейтинг. А поисковые системы знают, что означает постоянная переадресация 301, поэтому они будут присваивать посещениям адресов с www и без www один и тот же рейтинг веб-сайтов.
Существует другой адрес приведет к плохой кэш-памяти. Когда страница имеет несколько имен, она может появляться в кэше несколько раз.
- Адрес перенаправления отслеживания браузера
- Сервер обрабатывает «запрос»
- Сервер возвращает HTML-ответ
- Браузер анализирует HTML и рисует страницу
- Браузер отправляет объекты, встроенные в HTML, такие как изображения, стили CSS, файлы JS, шрифты и т. д.
- Браузер отправляет запрос Ajax
Из вышеизложенного мы уже знаем весь общий процесс от ввода URL до отображения страницы.Далее мы снова подробно разберем несколько ключевых шагов.
Процесс разрешения DNS
Как мы уже знаем выше, мы вводим URL-адрес и инициируем запрос.По сути, конечный сервер, который получает запрос, и каждый сервер имеет IP-адрес, поэтому обычно одно доменное имя соответствует одному IP-адресу (есть также соответствующие несколько IP-адресов, Мы временно анализируем ситуацию с IP-адресом), но как браузер узнает, какому IP-адресу соответствует доменное имя? Разрешение доменного имени в основном представляет собой следующий процесс:
- Поиск через кеш браузера.Если он существует в кеше, то продолжать поиск не будет.Используя найденный IP-адрес напрямую, мы можем найти его вChromeчтобы увидеть все кэшированные DNS в нашем браузере.
- Если браузер не находит его, мы проверим, сохранена ли соответствующая информация о доменном имени на нашем компьютере.
- Если он не сохранен локально, он будет искаться с маршрутизатора.
- Если маршрутизатора нет, он отправится к провайдеру, чтобы найти
Из вышеприведенного анализа нам нужно выполнить разрешение DNS, чтобы найти IP-адрес при вводе доменного имени, но в нашем коде мы часто помещаем некоторые статические ресурсы в CDN, и нам нужно выполнять разрешение DNS для каждого адреса CDN, что быть пустой тратой времени, мы можем выполнить DNS-разрешение заранее, и тогда, когда запрос будет сделан, DNS будет разрешен и нет необходимости ждать.
<!--在head标签中,越早越好-->
<link rel="dns-prefetch" href="//example.com">
TCP-соединение
Справочная статьяExploring Network Series (1) - Трехстороннее рукопожатие TCP и дерево рендеринга Рендеринг страницы => Процесс от входного URL до отображения страницы?
Первое рукопожатие: установление соединения
Клиент отправляет сегмент запроса на соединение, значение SYN равно 1, порядковый номер x. Клиент переходит в состояние SYN_SEND, ожидая подтверждения сервера.
Второе рукопожатие: сервер получает сегмент SYN
Когда сервер получает сегмент SYN от клиента, ему необходимо подтвердить сегмент SYN и установить для номера подтверждения значение x+1 (порядковый номер+1). В то же время вы должны сами отправить сообщение запроса SYN, установить значение SYN равным 1, а Sequence Number — y. Сервер помещает всю указанную выше информацию в сегмент (т. е. сегмент SYN+ACK) и вместе отправляет ее клиенту, после чего сервер переходит в состояние SYN_RECV.
Третье рукопожатие: клиент получает сегмент SYN+ACK
После получения сегмента SYN+ACK от сервера клиент устанавливает номер подтверждения равным y+1 и отправляет сегмент ACK на сервер.После отправки сегмента и клиент, и сервер переходят в состояние ESTABLISHED и завершают TCP Три рукопожатия.
После завершения трехэтапного рукопожатия клиент и сервер начинают передавать данные.В описанном выше процессе есть несколько важных понятий:
Неподключенная очередь: в протоколе трехстороннего рукопожатия сервер поддерживает неподключенную очередь, которая открывает запись для каждого SYN-пакета клиента (syn=j), что указывает на то, что сервер получил SYN-пакет и отправляет подтверждение клиенту. , ждет пакет подтверждения клиента. Соединение, идентифицируемое этими записями, находится на сервере в состоянии Syn_RECV.Когда сервер получает пакет подтверждения от клиента, запись удаляется, и сервер переходит в состояние ESTABLISHED. Параметр невыполненной работы: указывает максимальное количество неподключенных очередей.
Количество повторных передач SYN-ACK: после того, как сервер отправил пакет SYN-ACK, если он не получил пакет подтверждения клиента, сервер повторно передаст его в первый раз.Если количество раз превышает максимальное количество повторных передач, указанное системы, система удаляет информацию о подключении из неподключенной очереди. Обратите внимание, что время ожидания для каждой повторной передачи не обязательно одинаково.
Время существования без подключения: относится к максимальному времени, в течение которого сохраняется запись в очереди без подключения, то есть максимальному времени для службы от получения пакета SYN до подтверждения того, что пакет недействителен.Это значение времени является самым длительным временем ожидания для всех пакеты запроса на повторную передачу общее время. Иногда мы также называем время выживания без подключения к сети Timeout и время выживания SYN_RECV.
Почему трехстороннее рукопожатие
В четвертом издании «Компьютерной сети» Се Сижэня цель «трехстороннего рукопожатия» состоит в том, чтобы предотвратить внезапную передачу на сервер недопустимого сегмента запроса на подключение, что приводит к ошибкам.
«Недопустимый сегмент запроса на подключение» формируется в такой ситуации: первый сегмент запроса на подключение, отправленный клиентом, не теряется, а остается на узле сети в течение длительного времени, что приводит к задержке Сервер не приходит до определенного времени после разрыва связи. Первоначально это был давно несуществующий сегмент. Однако после того, как сервер получает недопустимый сегмент запроса на подключение, он ошибочно полагает, что это новый запрос на подключение, отправленный клиентом снова. Таким образом, он отправляет сегмент подтверждения клиенту и соглашается установить соединение. Предполагая, что «трехстороннее рукопожатие» не используется, пока сервер отправляет подтверждение, устанавливается новое соединение. Поскольку клиент сейчас не выдал запрос на установление соединения, он проигнорирует подтверждение сервера и не отправит данные на сервер. Но сервер считает, что установлено новое транспортное соединение и ждет, пока клиент отправит данные. Таким образом, многие ресурсы сервера тратятся зря. Подход «трехстороннего рукопожатия» может предотвратить возникновение описанного выше явления. Например, в данном случае клиент не будет выдавать подтверждение серверу. Поскольку сервер не получает подтверждения, он знает, что клиент не запрашивал установку соединения. "
Добавить Автора
Связь:Ууху. Call.com/question/24…
Источник: Чжиху
Почему он машет четыре раза?
Справочная статьяЧетыре волны TCP (иллюстрация) - Почему четыре волны
Протокол TCP — это ориентированный на установление соединения, надежный протокол связи транспортного уровня на основе потока байтов. TCP является полнодуплексным режимом, что означает, что когда узел 1 отправляет сегмент FIN, это просто означает, что у узла 1 нет данных для отправки, и узел 1 сообщает узлу 2, что все его данные были отправлены; однако, в это время, хост 1 все еще может принимать данные от хоста 2; когда хост 2 возвращает сегмент ACK, это означает, что он уже знает, что хост 1 не имеет данных для отправки, но хост 2 все еще может отправлять данные на хост 1; когда хост 2 также Сегмент FIN отправлен, это означает, что у хоста 2 нет данных для отправки, и он сообщит хосту 1, что у меня нет данных для отправки, а затем они с радостью разорвут TCP-соединение. Если вы хотите правильно понять принцип четырех расставаний, вам нужно понять изменения состояния во время четырех расставаний.Автор: Ли Тайбай не белый
Источник: ЦСДН
оригинал:blog.CSDN.net/Big Odd/Ах…
- Когда хост 1 отправляет сообщение FIN, он просто сообщает хосту 2, что у меня больше нет данных для отправки, но я все еще могу получать данные от хоста 2 (впервые)
- Когда узел 2 отправляет сообщение, он просто сообщает узлу 1, что я получил сигнал и знаю, что у вас больше нет данных для отправки, но узел 2 все еще может продолжать отправлять данные узлу 1 (во второй раз).
- Когда у хоста 2 действительно нет данных для отправки на хост 1, он отправит сообщение на хост 1, сообщая хосту 1, что у меня нет данных для отправки (в третий раз).
- После того, как хост получает пакет, снова отправляет пакет на хост-компьютер 2, разъем может отключить это (четвертый)
Суммировать
Из приведенного выше анализа видно, что каждый раз, когда запрашивается ресурс, требуется TCP-соединение.Будет трехсторонняя операция рукопожатия, чтобы указать, что соединение успешно.После успешного соединения сервер отправит данные к клиенту.Если подключение происходит каждый раз, когда запрашивается ресурс, это пустая трата времени. Мы можем подключиться заранее, прежде чем запрашивать ресурсы. Когда будет сделан реальный запрос, он уже подключен. Мы можем отправить ресурсы раньше. Мы можем использовать следующие методы:
Справочная статьяdns-prefetch, preconnect, prefetch и prerender в теге Head
<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>
Браузер выполняет следующие действия:
- Интерпретируйте значение атрибута href, если это допустимый URL-адрес, затем продолжите оценку того, является ли протокол URL-адреса http или https, в противном случае завершите обработку.
- Если текущий хост страницы отличается от хоста в атрибуте HREF, Crossorigin фактически устанавливается анонимным (то есть без файлов cookie). Если вы хотите принести информацию, такую как файлы cookie, вы можете добавить атрибут Crossorignign, а Crossorigning эквивалентен Чтобы установить его для использования-учетных данных
Обработка кеша
Мы установили TCP-соединение, и сервер уже может отправлять ресурсы клиенту (браузеру), но если ресурс был запрошен один раз, но мы обновляем страницу, нам нужно повторно запросить ресурс, что слишком расточительно. запросы. , браузер снова разрешает запрос с помощьютайникСтратегия кэширования заключается в повторном запросе ресурсов, и попытке получить лучшее из ресурсов, которые были запрошены, чтобы уменьшить количество запросов, то есть нет необходимости снова делать TCP-соединение, но если браузер каждый раз проверяет независимо от того, есть ли уже ресурс в кеше, его не будет Повторный запрос также приведет к тому, что ресурсы, которые мы получим, не будут актуальными, поэтому для этих двух ситуаций кеш браузера имеет следующие две стратегии:
- Сильный кеш
- Кэш согласования (слабый кеш)
Кратко проанализируем обе стратегии.
Сильный кеш
Справочная статьяО чем мы говорим, когда говорим о кэшировании HTTP
Сильное кэширование в основном оценивается браузером по двум полям в заголовке запроса:
- expires
- cache-control
Сильное попадание в кеш из кеша памяти и из кеша диска
Во время теста, когда я увидел сильное попадание в кеш, было два состояния: 200 (из кеша памяти) кеша и 200 (из кеша диска), поэтому я искал разницу между ними:
- кэш памяти: Храните ресурсы в памяти и получайте их из памяти.
- дисковый кеш: кешировать ресурсы на диск и получать их с диска. Самая большая разница между ними заключается в том, что при выходе из процесса данные в памяти будут очищены, а данные на диске — нет.
На самом деле, если мы запросим несколько одинаковых ресурсов изображения на одной странице, браузер автоматически обработает это и автоматически получит из кеша памяти (из кеша памяти), но если мы закроем страницу или обновим страницу, то кеш памяти станет недействителен.Однако этот кеш автоматически обрабатывается браузером для нас, и мы ничего не можем сделать.
expires
expires — это функция в http 1.0.Она используется для определения того, истекает ли срок действия ресурса, путем указания абсолютного времени истечения срока действия кеша по Гринвичу для ресурса.Если он не истекает, используется кеш, в противном случае ресурс запрашивается повторно.
Недостаток: из-за использования определенного времени, если представление времени неверно или не преобразовано в правильный часовой пояс, это может вызвать ошибку времени жизни кэша.
cache-control
Кэш-Контроль даhttp1.1
Чтобы компенсировать дефекты Expires, когда Expires и Cache-Control существуют одновременно, Cache-Control имеет более высокий приоритет, чем Expires.
Ниже мы разберемcache-control
Конфигурация:
Атрибуты | описывать |
---|---|
max-age | Устанавливает максимальный период хранения кеша, по истечении которого кеш считается просроченным (в секундах).cache: max-age=60 вот 60 секунд |
public/private | общедоступный означает, что и сторона сервера, и сторона браузера могут кэшировать,cache: max-age=60, public , частный означает, что только браузер пользователя может кэшировать, маршрутизатор имеет CDN и не может кэшировать |
no-cache | no-cache не означает отсутствие кеширования, но его нужно запросить у сервера один раз.Если кеш еще действует, то сервер вернет только 304 и не вернет соответствующее тело запроса.Запрос не будет уменьшен , но запрошенные ресурсы могут быть уменьшены (в стратегии кэширования Express, если заголовок запроса содержитcache-control и установитьno-cache Тогда вернут только новые ресурсы, а 304 не вернут) |
no-store | Нет кэширования, используйте согласованное кэширование |
must-revalidate | Кэши должны проверять состояние старых ресурсов перед их использованием, а ресурсы с истекшим сроком действия использовать нельзя. |
Если cache-control указывает, что срок действия ресурса истек, или установлено no-store, это не значит, что кешированный ресурс больше нельзя использовать, и браузер также может использовать его вместеСогласовать кеш, давайте проанализируем кеш согласования
Согласовать кеш
Если ресурс сильного кеша (управления кешем) выходит из строя, браузер вызывает согласованную стратегию кэширования.Стратегия согласованного кэширования в основном обрабатывается следующими двумя заголовками запроса:
- last-modified (if-modified-since) -> http 1.0
- Etag(if-none-match) -> http 1.1
last-modified
Когда браузер запрашивает ресурсы сервера, сервер присваивает время последней модификации файла соответствующему заголовку запроса.last-modified
, например: last-moified: Fri, 08 Jun 2018 10:2:30:GMT
При повторном запросе ресурса (обновление страницы (не принудительное обновление F5 + Ctrl) или повторное открытие страницы) в заголовок запроса будет добавленоif-modified-since
, значение которогоlast-modified
значение, такое как: if-modified-since: пт, 08 июня 2018 г. 10:2:30: GMT, отправленное на сервер, сервер будет судить, действителен ли кеш в соответствии с этим значением, если кеш все еще действителен, он вернет304, и пустое тело ответа, браузер прочитает из кеша, иначе вернет200и вернуть результат запроса
Etag
По сути, Etag имеет тот же эффект, что и last-modified.Это идентификатор, возвращаемый серверной частью для соответствующего ресурса, за исключением того, что last-modified — это время последней модификации ресурса, а etag — соответствующий идентификатор Стратегии разных серверов для генерации etags различны. Например, правила экспресс-фреймворка для генерации etags:文件最后一次修改时间-文件的大小
function stattag (stat) {
// mtime 文件最后一次的修改时间
// size 文件的大小
var mtime = stat.mtime.getTime().toString(16)
var size = stat.size.toString(16)
return '"' + size + '-' + mtime + '"'
}
При повторном запросе ресурса (обновление страницы (не принудительное обновление F5 + Ctrl) или повторное открытие страницы) в заголовок запроса будет добавленоif-none-match
Заголовок запроса отправляется на сервер, и сервер будет судить, действителен ли кеш в соответствии с этим значением.Если кеш все еще действителен, он вернет304, и пустое тело ответа, браузер прочитает из кеша, иначе вернет200Запрос и возвращает результат.
Из анализа вышеlast-modified
а такжеetag
Функция должна быть та же.Почему понятие etag появляется в HTTP 1.1?Etag в основном решает следующие проблемы:
- Некоторые файлы могут не менять содержимое (меняется только время модификации), в это время мы не хотим, чтобы файл перезагружался. (Значение Etag вызовет кеширование, а Last-Modified не сработает) (Из правил etag, сгенерированных экспрессом, этой проблемы не существует)
- Детализация, которую может проверить If-Modified-Since, составляет секунды.Когда модификация очень частая, Last-Modified активирует кеш, но значение Etag не будет запускаться и перезагружаться.
- Некоторые серверы не могут получить точное время последней модификации файла.
Если оба установленыlast-modified
а такжеetag
лейбл, у кого выше приоритет?
Если оба установленыlast-modified
а такжеetag
Лейбл, у кого тогда выше приоритет? Правило состоит в том, что etag имеет приоритет, так почему же etag имеет приоритет над last-modified? Это определяется браузером?
После анализа он не определяется браузером, а имеетсерверрешенный. Браузер передает его только при запросе ресурсаlast-modifiedа такжеetagЗаголовок запроса отправляется на сервер, а затем сервер решает, доступен ли кеш или нет.Мы можем проверить следующееexpressИсходный код логики обработки для анализа:
if (this.isCachable() && this.isFresh()) {
this.notModified()
return
}
вthis.notModified()
просто верни304
:
SendStream.prototype.notModified = function notModified () {
var res = this.res
debug('not modified')
this.removeContentHeaderFields()
res.statusCode = 304
res.end()
}
Основная логика экспресс-оценки эффективности кэша заключается вthis.isFresh()
Реализовано в методе:
function fresh (reqHeaders, resHeaders) {
// fields
var modifiedSince = reqHeaders['if-modified-since']
var noneMatch = reqHeaders['if-none-match']
// unconditional request
if (!modifiedSince && !noneMatch) {
return false
}
// Always return stale when Cache-Control: no-cache
// to support end-to-end reload requests
// https://tools.ietf.org/html/rfc2616#section-14.9.4
var cacheControl = reqHeaders['cache-control']
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {
return false
}
// if-none-match
if (noneMatch && noneMatch !== '*') {
var etag = resHeaders['etag']
if (!etag) {
return false
}
var etagStale = true
var matches = parseTokenList(noneMatch)
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
etagStale = false
break
}
}
if (etagStale) {
return false
}
}
// if-modified-since
if (modifiedSince) {
var lastModified = resHeaders['last-modified']
var modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince))
if (modifiedStale) {
return false
}
}
return true
}
Мы можем проанализировать его в соответствии с приведенным выше кодомexpress
Как определить, действителен ли кеш
- Если заголовок запроса не содержит
if-modified-since
а такжеif-none-match
заголовок, он напрямую решает, что кеш недействителен
var modifiedSince = reqHeaders['if-modified-since']
var noneMatch = reqHeaders['if-none-match']
if (!modifiedSince && !noneMatch) {
return false
}
- Если в заголовке запроса есть
cache-control
И есть настройкиno-cache, то непосредственно определите инвалидацию кеша (var CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/
)
var cacheControl = reqHeaders['cache-control']
//
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {
return false
}
- затем судить
if-none-match
, способ судить состоит в том, чтобы повторно получитьetag
, а потом судитьif-none-match
будь то сetag
Равны, если не равны, напрямую судить кэш аннулированию
// if-none-match
if (noneMatch && noneMatch !== '*') {
var etag = resHeaders['etag']
if (!etag) {
return false
}
var etagStale = true
var matches = parseTokenList(noneMatch)
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
etagStale = false
break
}
}
if (etagStale) {
return false
}
}
вvar etag = resHeaders['etag']
извлекается по запросуetag
- окончательное решение
if-modified-since
, логика его суждения такова, что еслиlast-modified
меньше или равноif-modified-since
значение, а затем напрямую определить аннулирование кеша
// if-modified-since
if (modifiedSince) {
var lastModified = resHeaders['last-modified']
var modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince))
if (modifiedStale) {
return false
}
}
Из вышеприведенного анализа видно, чтоExpress
тайниквступить в силумеханизм не соблюдаетсяetag
приоритет выше, чемlast-modified
, но судитьневерныйМеханизм следующийetag
приоритет выше, чемlast-modified
.
Продолжать...