Не поставил favicon на сайте — получи двойной трафик от Chrome

Впервые за пять лет разработки интернет-сайтов я столкнулся с весьма неожиданной проблемой, стоившей мне многих часов поиска, нервов и волос на голове. Внезапно я обнаружил, что на новом сайте, который у меня сейчас в разработке на локалхосте, дублируются INSERT запросы к БД. Отправляю один комментарий через форму, а в базу вставляются два. Если вы не знаете, как связана эта проблема с Chrome, favicon.ico и ModRewrite, то добро пожаловать под кат.

Нет, это не в глазах двоится


Разумеется, первое что пришло в голову — где-то в скрипте «двоится» запрос к базе данных. Например, такая «школьная ошибка»:

$query = "INSERT INTO table VALUES(...)";
$result = mysqli->query($query);
if ($result = mysqli->query($query)) {
...
}

Проверяю сто тысяч раз — нет. С кодом все в порядке. Значит браузер по какой-то причине отправляет данные дважды. Генерирую простенький тестовый скрипт с инкрементом сессионной переменной и — да! Вместо увеличения на единицу браузер упорно показывает увеличение переменной на два. Пробую вместо Хрома Сафари — нет такой проблемы. Далее идут поиски некорректно работающих яваскриптов, расширений для браузера, но все безрезультатно. Поиск по интернету давал схожие советы. Многие программисты в аналогичной ситуации вводили проверку на уникальность и отсекали дублирующиеся посты. Но баг то это решение не устраняет и я решил не останавливаться и найти причину такого поведения.

Наконец, причина была найдена. И так как решение нашел с трудом, на англоязычной девелоперской ветке, хочу поделиться им здесь. Все просто — виновника два: Google Chrome (и производные от него браузеры) и mod_rewrite в Apache.

Суть проблемы


Все просто. Особенность браузеров, построенных на платформе Crome в том, что они ищут файл favicon.ico для каждого сайта. И если его нет, то они все равно будут упорно его искать. При каждом обновлении страницы, отдельным запросом к серверу. А большинство .htaccess файлов в Apache имеют в себе строчки:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . /index.php

Руководствуясь этим правилом, получая запрос от браузера к несуществующему favicon.ico Apache послушно перенаправит его к файлу index.php и скрипт отработает дважды. Конечно, в большинстве случаев полноценные веб-приложения имеют проверку на уникальность и не пропускают повторяющиеся запросы. А сайты в большинстве случаев имеют файл favicon.ico. Но все же раз существует такая проблема, значит можно описать методы решения.

Варианты решения


Решение первое, самое простое: заведите на сайте favicon.ico. Chrome найдет его и успокоится.
Решение второе — немного изменить файл .htaccess на сервере. У меня блок правил mod_rewrite для всех проектов теперь будет выглядеть так:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
# Next line is to solve the Chrome and favicon.ico file issue.
# Without it browser sends two requests to script.
RewriteCond %{REQUEST_FILENAME} !favicon.ico
# RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

Не поставил favicon? Получи двойной трафик от Chrome


Если вдуматься, то утверждение справедливо, ведь вместо того, чтобы отдать «статику», сервер будет запускать веб-приложение, которое в лучшем случае вернёт в браузер вместо иконки динамически сгенерированную 404 страницу. А в худшем — отработает полностью запуск главной страницы сайта. Получается, что не установив иконку на сайте (например, на одном популярном блоговом движке), разработчик вдвое увеличивает нагрузку на сервер от пользователей Google Chrome.

Вот, собственно и все. Надеюсь эта информация кому-то пригодится и сэкономит время.
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 60

    +34
    Тут скорее отсутсвие обработки запросов, которые должны 404 страницу возвращать, а не отсутствие фавикона.
    Ну, т.е. странно что обращение к "/favicon.ico" и "/comment/" какой-нибудь обрабатываются одинаково
      –20
      Во-первых, есть кейс, когда пользователя с 404 лучше отправить на home page, чтобы он не ушел. Во-вторых, иногда даже 404 страница должна быть не просто статикой (редко, но бывает).

      В результате любой 404 может превратиться в нагрузку на сервер.
      Проблема для продакшена не частая — почти любой сайт имеет favicon, но тем не менее.
        +5
        Ну ок, в случае когда идет отправка на главную — то получится редирект и дополнительный показ главной страницы, но никак не добавление комментария. Ну ок, в случае когда 404 не просто статика, то все-равно это страница 404, а не добавление комментария в базу.
          –4
          Думаю, автор пример добавления в базу добавил просто для того, чтобы показать «ААА, Мы все умрем!», и судя по-всему, его за это и минусуют.

          Но дополнительный запрос на главную с поднятием и выполнением кучи кода, иногда даже с хождением в базу будет.
          +1
          Во-первых, есть кейс, когда пользователя с 404 лучше отправить на home page, чтобы он не ушел. Во-вторых, иногда даже 404 страница должна быть не просто статикой (редко, но бывает).

          Ну так делайте динамический переход с помощью JavaScript.
            –2
            Иногда js в браузере может быть выключен и иногда все же есть требования, чтобы сайты работали без js. Да, это не можно, не круто, но иногда бизнесу так выгоднее. А соответственно js редирект не будет работать.

            А еще есть прикол с Referer при редиректе через js (иногда это тоже важно). При серверном редиректе referer нового запроса будет первоначальная страница, а при js редиректе — текущая страница.
              0
              Иногда js в браузере может быть выключен и иногда все же есть требования, чтобы сайты работали без js

              Для этого существует тэг noscript. Включены скрипты — пользователь видит пустую страницу, с которой тут же идёт переход. Выключены скрипты — пользователь получает простую статическую страницу.

              А еще есть прикол с Referer при редиректе через js (иногда это тоже важно). При серверном редиректе referer нового запроса будет первоначальная страница, а при js редиректе — текущая страница.

              Это случится, если при некорректном запросе сначала делать редирект на страницу 404, а не отдавать код сразу.
                –2
                > Для этого существует тэг noscript.
                — пользователь пришел на 404.html и ушел, это не то, что он ожидал увидеть и ничего, что привлекло бы его внимание тут нету. пользователь ушел.

                серверный редирект позволит сразу его перекинуть на хомпейдж, и там уже есть вероятность, что он задержится дольше, чем на 404.html

                А решение проблемы с /favicon очень простое — нужно правильно настроить рулы редиректа. Для всех картинок прописать 404, без редиректа, а для 404 при запросе html — прописать редирект на хомпейдж. Велосипедов придумывать не нужно, все уже давно известно.

                > Это случится, если при некорректном запросе сначала делать редирект на страницу 404, а не отдавать код сразу.

                сценарий:
                — пользователь из внешнего источника «А» тыкает на ссылку, которая ведет на отсутствующую страницу (почему отсутствует — другой вопрос).

                страницы нету — он попадает на 404.html, на которой стоит редирект на хомпейдж. перейдя на хомпейдж — у него в referer будет 404.html, а не «ресурс А»

                единственный способ сохранить referer — серверный редирект.
                  +1
                  А решение проблемы с /favicon очень простое — нужно правильно настроить рулы редиректа.

                  Да, это более правильный способ.

                  страницы нету — он попадает на 404.html

                  А зачем делать редирект на 404.html, когда можно просто отдать код страницы?
                  Тогда в Referer будет не 404.html, а адрес некорректного запроса. Если же позарез нужен именно «ресурс A», тогда можно его передавать через параметры запроса.

                  Впрочем, возни с сервисами аналитики это прибавит.
                    0
                    > А зачем делать редирект на 404.html, когда можно просто отдать код страницы?

                    Спасибо, интересный вариант, про него не подумал про него!

                    Но есть 2 «но»:
                    1. Отдав контент и далее сделав редирект — Referer у редиректа будет запрошенная страница (которой нету).
                    2. Этот вариант займет больше времени. Сначала нужно доставить контент (объем больше, чем у пустого редиректа), потом нужно будет этот контент отрендерить, и лишь потом будет выполнен редирект.
                    • UFO just landed and posted this here
                        0
                        Кажется вы не к тому комменту ответили. Ваши слова лишь подтверждают мои.

                        Моя позиция в том, что для сохранения referer нужно делать 3xx редирект html контента.

                        В сообщениях выше по этой ветке коллеги предлагают отдать контент и уже из контента делать редирект. Отдать html контент с 404 кодом, даже если и получится, то это не правильно.

                        И если таки отдать 404.html — то при редиректе с нее в реферер будет установлен 404.html, а не ресурс, с которого пришли.
                    0

                    Все-таки лучше не сразу отправлять на главную, а показать 404 и, если уж так надо, то добавить мета-тег c [http-equiv="refresh"], чтобы пользователя перекидывало на главную через несколько секунд (работает без JS):


                    <meta http-equiv="refresh" content="3; URL=http://example.com/"> 
                      0
                      Кстати, ещё одна причина отдать именно страницу 404 — банальное следование стандартам. При некорректном запросе вы должны вернуть HTTP-ответ с кодом 4**, иначе будете вводить как браузеры, так и поисковые страницы в заблуждение.
                      • UFO just landed and posted this here
                        • UFO just landed and posted this here
                  0
                  Судя по минусам, мой коммент не поняли.

                  Запись в базу — это тупость, без сомнения. Судя по всему, автор хотел нагнать паники «Мы все умрем».

                  А так же бизнес кейсы бывают разные. Не все делается на фронтенде, как бы этого не хотелось многим.
                    +8
                    Какой Home page по 404, если речь о favicon.ico? Вы на запрос иконки собираетесь отдавать html?
                    Банально у автора неправильно настроена обработка путей. Это довольно частая проблема, среди тех кто любит бездумно все запросы заворачивать на index.php.
                      0
                      Я не собираюсь уже поверьте.

                      Но вот есть множество проектов, которые сделаны на столько коряво, что и на /favicon отдается 404.html и даже на ошибочный рест запрос тоже 404.html словить можно.
                    0
                    Это обычное дело для систем типа Wordpress, где ЧПУ формируются автоматически. Вместо того, чтобы в htaccess описывать все возможные маски названий страниц, происходит перенаправление на index, а дальше тот сам в базе ищет соответствующий пост.
                    +26
                    Мне кажется, статья несколько неполная. Например, заголовок я бы дополнил так:
                    Не поставил favicon на сайте, халтурно сокнфигурировал Apache и по-дурацки организовал работу скриптов и БД — получи двойной трафик от Chrome

                    А одно из предложений в тексте так:
                    … большинство .htaccess файлов в Apache, которые я написал для своих сайтов, имеют в себе строчки...
                      +10
                      Не, двойной трафик от Chrome, а вот двойной INSERT уже от собственных кривых рук.
                        0
                        Это в общем-то стандартный конфиг Wordpress, на котором крутится до 25% сайтов в мире.
                        +20
                        Не совсем понимаю: запрос на favicon делает через GET. Вставка данных, в современном мире, делает через POST. Мне кажется, автор сам себе прострелил ногу.
                          0
                          Может у них там статистика какая-то своя считается по заходам на страницу, например.
                            0
                            И по загрузке статики?
                              +1
                              Если все запросы заворачиваются на Index.php то да, халтурно настроенный апач) в вверху же написали.
                            +1
                            Я даже специальное слово по такому случаю вспомнил — идемпотентный.
                            +6
                            У меня лыжи не едут.
                            Почему две вставки происходит? Хром обратится GET-запросом напрямую к /favicon.ico. Никаких данных в POST или GET быть не должно. С чего вдруг двойное выполнение insert? Тогда получается любой 404ый запрос с этой страницы может косячить, даже аякс?
                              +1
                              Автор кривым .htaccess завернул все 404(включая статику) на index.php в итоге получил многократное выполнение основной логики.
                              +21
                              Как укусить себя за задницу, для чайников. Издание 19283712893, переработатнное и дополненное.
                                –7
                                Как укусить себя за задницу
                                … в прыжке с сальто, на втором обороте тройного тулупа.
                                +5
                                > Внезапно я обнаружил, что на новом сайте, который у меня сейчас в разработке на локалхосте, дублируются INSERT запросы к БД. Отправляю один комментарий через форму, а в базу вставляются два.

                                Реквестирую подробности рассказа о том как комментарии в базу вставляются по GET-запросу да ещё и без дополнительных данных.

                                А вообще, нагрузку увеличить Хром и правда может не только как тут в комментах уже писали о user-friendly 404, но и при наборе в адресной строке конкретного адреса: prefetch-логика шлёт два GET-запроса, что дважды загружает страницу, инкрементирует дважды счётчики, даже может сбросить выделение непрочитанных данных в некоторых случаях.
                                  0
                                  Не узнавали, как от этого избавиться? Жутко бесит при тестировании.
                                    0
                                    Chrome: Settings –> Privacy –> Use a prediction service… (снять оба чекбокса)
                                  +5
                                  Помимо вышенаписанного, это что же получается — автор перед тем, как отправиться гуглить, даже не заглянул в access.log? Ну или в мониторинг сети в самом хроме, в конце-то концов.
                                    +11
                                    Все верно: сделал говносайт через ж… — получи траф.
                                    Вы мне лучше расскажите, почему попадание на несуществующую страницу увеличивает счетчик заходов? Пользователь никуда не попал (404 == страницы нет)? Какая в этом логика подсчета? Хотите редирект — ок, перенаправьте и там засчитайте.

                                    Завтра к вам зайдут с iPhone/iPad и он запросит картинку apple-touch-icon.png. Еще строчку добавите в .htaccess?
                                    Вот где проблема: вы не хотите исправить кривое решение, а хотите обставить костылями по кругу.
                                    Жду еще 20 статей такого же рода, про каждый новый тип картинки.
                                      0
                                      Ну в принципе 404 ошибки тоже неплохо считать, можно выловить неправильные ссылки и т.п.
                                        0
                                        Да, и поэтому их надо считать отдельно.
                                      +2

                                      Вся проблема кроется в Crome

                                        +1
                                        1 апреля уже? Пятница 13? Вы поржать или попугать? :) Без исходников вашего велосипеда(«движка») и настроек апача(.htaccess), тщательно проведенного анализа — кто сюда это пропустил?
                                        А по делу:
                                        1. плохо настроен modrewrite (10%);
                                        2. Ошибка в написании велосипеда, а именно в обработке «404 Not found» — скорее при обработке передаются все параметры запроса и если автор обрабатывает параметры в одном месть, то параметр на событие повторяется (например, index.php?act=comment) — не обязательно get, скорее всего запрос обрабатывается, через Request.
                                          0
                                          В firefox почти такая-же проблема. Там дублируется запрос лишь к favicon.ico.
                                            0
                                            Интересно, что проблема эта — кроссерверная. Обнаруживается даже на моём самописном сервере.
                                            0
                                            Помимо всего вышеописанного, стоит добавить, что заголовки Last-Modified, ETag, Cache-Control и Expires — для зануд, пусть поисковые роботы сами проверяют когда у меня контент обновится.
                                              +5
                                              Ждем серию постов от ТС о проблемах, возникших после того, как сайт стал виден из WWW…
                                                +3
                                                Меня больше потрясло то, что вы по сути по любому урлу без проверки, что именно вообще было запрошено, делаете инсерт в базу! Просто клондайк для DDOS. А как же css, js и другая статика?
                                                Статья должна называться: «как я несколько лет косячил с настройками apache и не проверял входящие данные в PHP».
                                                Я открою вам секрет: не только в Chrome так. Многие браузеры во время совершения ajax запроса шлют сначала OPTION запрос для принятия заголовков Allowed-*, например.
                                                Пересмотрите свое отношение к написанию кода, я прямо чувствую там дуршлаг )
                                                  +2
                                                  Не поставил favicon на сайте — написал статью на хабре

                                                  Я бы посоветовал почитать книжки про сетевой стек, поизучать как запросы вообще приходят на сервер, используя протокол HTTP, как можно эти запросы анализировать и делать выводы, а не «Далее идут поиски некорректно работающих яваскриптов, расширений для браузера, но все безрезультатно. Поиск по интернету давал схожие советы. Многие программисты в аналогичной ситуации вводили проверку на уникальность и отсекали дублирующиеся посты.»

                                                  Еще в Developer Tools есть замечательная вкладка Network, где можно посмотреть все запросы, которые отправляет браузер
                                                  • UFO just landed and posted this here
                                                      +2
                                                      А потом появляются вопросы почему все смеются над phpшниками…
                                                        –1
                                                        Кто-то еще пишет sql запросы руками? :(
                                                          0

                                                          Соглашусь с комментаторами выше, почему на 404 GET запросе происходит insert?


                                                          Если вы пишите свою систему статистики посещений, то:


                                                          • во-первых не стоит записывать каждый HTTP запрос (Это уже не статистика а логгер, который легко настроить на HTTP сервере).
                                                          • во-вторых исключите обработку своим index.php запросы на статику по паттернам (прим. /assets/*, /favicon.ico, /robots.txt, /sitemap.xml), даже если файлов таких нет, в дальнейшем вы избежите подобных проблем.
                                                          • в-третьих никакого двойного трафика нет, у вас так-же происходят запросы на получение js, css и т.п

                                                          .З.Ы. Помню была немного похожая ситуация с хромом:
                                                          Писал платежные шлюзы которые тестировались через GET запрос аля: http://{ip}/gatewayName.php?params....
                                                          через какое-то время обнаружил что один шлюз начал переодически самопроизвольно запускаться, после долгого исследования обнаружил что хром добавил url в стартовую панель и переодически пытался обновить preview страницы.

                                                            0
                                                            Коллеги, спасибо за то, что уделили внимание и обсудили мою статью. Спасибо за все плюсы и минусы. Некоторые комментарии оказались действительно полезными — я не считаю себя «гуру» программирования и стараюсь развиваться.

                                                            На язвительные комментарии отвечу, что речь в статье шла о необычном поведении браузера Chrome (другие браузеры не особо докапываются до отсутствия иконки) и о том, к чему это может привести в случае некорректно настроенного .htaccess на сервере. (А он такой по дефолту во многих open source движках, например Wordpress, так что думаю, проблема может оказаться распространенной). Например, я бы ожидал столкнуться с отсутствующими файлами в upload директории сайта, ну или в папке с темой оформления, но забыл бы про корень сайта. Статья никоим образом не должна иллюстрировать грамотные подходы к созданию современных сайтов.

                                                            А про SELECT — тестировал новый метод класса на локалхосте. Сознательно вбил его вызов напрямую в index.php и в браузере F5, F5, F5… Только ради теста. Не надо придавать этому значения.
                                                              0
                                                              Кстати, это уже было (в сиспсонах) на хабре: https://habrahabr.ru/post/140693/
                                                                –1
                                                                Не буду разводить на холиваров на тему апача в 2к16, лампа на локалке дело обычное, но действительно пугают следующие вещи:
                                                                — конфиг mod_rewrite;
                                                                — INSERT в БД по GET запросу;
                                                                — INSERT в БД без CSRF;
                                                                — определение причинно-следственной связи.

                                                                Школьнику/студенту еще простительно, но 5 лет разработки в вебе?

                                                                ps. Почитал комментарии. Товарищам, считающим, что ТС добавлял записи для счетчика просмотров — прошу прочитать первый абзац в статье чуточку внимательнее, где явно указано про форму комментариев. Хотя и с прямым взаимодействием (вставка/обновление) с БД при реализации счетчика просмотров я бы поспорил.
                                                                  0
                                                                  Тема сисек почему по другому адресу по другому методу добавлялись комменты не раскрыта.

                                                                  А конфиг-то дефолтный, наверное, для большинства систем с единой точкой входа :)
                                                                    0
                                                                    Такая же проблема с background-image: url() если картинка отсутсвует.
                                                                      0
                                                                      Такая же проблема с любым запросом к отсутствующему ресурсу :)
                                                                        0
                                                                        Ну почти, если ссылка на картинку не правильная, то да.
                                                                        А если там по какой-то причине внезапно оказалось background-image: url(''), то не совсем очевидно :)
                                                                          0
                                                                          Хм, но это все равно, что:
                                                                          <a href="">link</a>
                                                                          

                                                                          — ссылка на текущую страницу (если такое написать в css файле, то это ссылка на css файл)
                                                                            0
                                                                            Да, но не все помнят.

                                                                    Only users with full accounts can post comments. Log in, please.