Как получить 100/100 Google PageSpeed Insights и не подать виду

Один из заказчиков попросил меня немного причесать сайт, заодно прописать цели для Яндекс.Метрики и Google Analytics. Между делом он рассказал мне, что предыдущим «оптимизаторам» удалось разогнать сайт до 100 баллов в PageSpeed Insight, мол, работа по оптимизации была долгая и значение упорно не хотело расти, но однажды подскочило до 100 и на этом закончили.

Я был немного удивлен услышанным, т.к. это был сайт на Joomla на бюджетном шаред-хостинге, с косяками адаптивной версии, неоптимизированными прозрачными PNG-шками по несколько мегабайт весом. В перерыве между работой ради интереса решил замерить баллы, правда через встроенный в Google Chrome Lighthouse.

33 балла в Lighthouse, странно

Опять переспросил заказчика, он говорит что показывает 100, но через сервис на сайте PageSpeed’а. И действительно.

А теперь самое интересное. Идем в index.php в корне сайта, и смотрим. Начало файла.

<? eval(gzuncompress(base64_decode('eAGdUtEKgjAU/ZUegilEIFNBYg/6UoQQ2IsVIWv6EkFgkfj37WwZIZtKL/dcvWfn3p3d+TrdJXG6P5HCizwahEHk04KcWVzXvHUu/FGFflFW4l5WDhE0I7MlaRF4nrxEg4wx4i56TPyX7JbnyCQT0JiYqkJRlmyA1AWIhgEM2voEtDGBUVN2xmE7Q3VAUDP+r9M5MtLp1wmrZ+Na2tGhabWDuJhiITE52PPb4uLHH0D3MhbmZnsD64jgRc9Dnl15jA/rG2rfQRnYDZRxZ+AEh0GbtBNK97tf7uoN8+vb5g==')));
eval(gzuncompress(base64_decode('eAG9kt1qg0AQhV8lF4IKvdDNmlaCF0npGmkMuLqrbClh/alKtlqsJiah7974ALG9SHs3MHM+Zs6Zt65K2rKuJlvDhBBo8H6mSKV6lri1aBp+VGQfYUKQSYlG/UhDK0wMIt/JHiiOMdDFmvQfDBQat009AcS6tK4ogncK08jNN541MvVXXIKc1tN3HRGjO4ZmycNeXL/iZhxbdAzgfUyXBQM0SGwq2GLMmeAXu+GA/idjfXIOG3To3CCfRo+wH+rn8mGIl+zoJtAxurzME0bOSOIYmHsGeuGgwQmSk+lSsNDL/dA48TCt05U7ov7ZlRvx1XmTtV1TTWL+mc3gNs2SOs0Uib9I5as6//oGaeX0oA==')));

Я первым делом подумал что сайт завирусован. Однако постороннего кода нигде не было, кроме как в index.php.

Чуйка и тут не подвела. Убираю посторонние вставки и опять проверяю скорость в PageSpeed.

Теперь все стало ясно. Для интереса все же решил разобрать обфусцированный код. Первый проход можно сделать через любой сервис для деобфусцификации, например:

<?php function _594420476($i) {
    $a = Array('SFRUUF9VU0VSX0FHRU5U', 'Q2hyb21lLUxpZ2h0aG91c2U=', 'SFRUUF9VU0VSX0FHRU5U', 'TmV4dXMgNQ==', 'SFRUUF9VU0VSX0FHRU5U', 'Q2hyb21lLUxpZ2h0aG91c2U=', 'SFRUUF9VU0VSX0FHRU5U', 'TmV4dXMgNQ==', 'SFRUUF9VU0VSX0FHRU5U', 'UFItQ1kuUlU=', 'SFRUUF9VU0VSX0FHRU5U', 'TW9iaWxl', 'SFRUUF9VU0VSX0FHRU5U', 'UFItQ1kuUlU=', 'SFRUUF9VU0VSX0FHRU5U', 'TW9iaWxl', 'SFRUUF9VU0VSX0FHRU5U', 'UGluZ2RvbVBhZ2VTcGVlZA==', 'SFRUUF9VU0VSX0FHRU5U', 'TW9iaWxl', 'SFRUUF9VU0VSX0FHRU5U', 'UFRTVA==', 'SFRUUF9VU0VSX0FHRU5U', 'TW9iaWxl', 'SFRUUF9VU0VSX0FHRU5U', 'UFRTVA==', 'SFRUUF9VU0VSX0FHRU5U', 'TW9iaWxl', 'LzIwNFwuMTg3XC4xNFwuKi8=', 'UkVNT1RFX0FERFI=', 'SFRUUF9VU0VSX0FHRU5U', 'R29vZ2xlIFBhZ2UgU3BlZWQgSW5zaWdodHM=', 'SFRUUF9VU0VSX0FHRU5U', 'TW9iaWxl', 'SFRUUF9VU0VSX0FHRU5U', 'R29vZ2xlIFBhZ2UgU3BlZWQgSW5zaWdodHM=', 'SFRUUF9VU0VSX0FHRU5U', 'TW9iaWxl');
    return base64_decode($a[$i]);
}
$GLOBALS['_1913565943_'] = Array(base64_decode('c3R' . 'y' . 'aXBvcw' . '=='), base64_decode('' . 'c3RyaX' . 'Bvc' . 'w=='), base64_decode('c' . '3' . 'Rya' . 'XBv' . 'cw=' . '='), base64_decode('c' . '3RyaXBvcw=='), base64_decode('c3Ry' . 'aXBvcw=='), base64_decode('' . 'c' . '3RyaXBvcw=='), base64_decode('c3Ry' . 'aXBvcw=='), base64_decode('c3R' . 'yaXBvcw=='), base64_decode('' . 'c3RyaX' . 'Bvcw' . '=='), base64_decode('c3R' . 'yaXBvcw=='), base64_decode('c3RyaXBvcw=='), base64_decode('c3Rya' . 'X' . 'Bvcw=' . '='), base64_decode('c3' . 'Rya' . 'XBvcw=='), base64_decode('c' . '3Ry' . 'a' . 'XBvcw=='), base64_decode('cHJl' . 'Z' . '19tYXRjaA' . '=' . '='), base64_decode('c3RyaXB' . 'vc' . 'w=='), base64_decode('' . 'c3Ry' . 'aXBvcw=='), base64_decode('' . 'c3Rya' . 'XBvcw=='), base64_decode('c3Ry' . 'aXBv' . 'cw=' . '='));

Уже лучше. Убираем конкатенации и пробегаемся через base64_decode.

if (stripos($_SERVER["HTTP_USER_AGENT") ], "Chrome-Lighthouse") && !stripos($_SERVER["HTTP_USER_AGENT" ], "Nexus 5")) {
    echo $p_0;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT"], "Chrome-Lighthouse") && stripos($_SERVER["HTTP_USER_AGENT" ], "Nexus 5")) {
    echo $p_1;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT" ], "PR-CY.RU") && !stripos($_SERVER["HTTP_USER_AGENT"], "Mobile")) {
    echo $p_0;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT"], "PR-CY.RU") && stripos($_SERVER["HTTP_USER_AGENT"], "Mobile")) {
    echo $p_1;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT"], "PingdomPageSpeed") && !stripos($_SERVER["HTTP_USER_AGENT" ], "Mobile")) {
    echo $p_0;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT"], "PTST") && stripos($_SERVER["HTTP_USER_AGENT" ], "Mobile")) {
    echo $p_1;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT"], "PTST") && !stripos($_SERVER["HTTP_USER_AGENT"], "Mobile""/204\.187\.14\.*/")) {
    echo $$p_0;
    die();
}
if (preg_match("REMOTE_ADDR", $_SERVER["HTTP_USER_AGENT" ])) {
    echo $p_0;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT" ], "Google Page Speed Insights") && stripos($_SERVER["HTTP_USER_AGENT" ], "Mobile")) {
    echo $p_1;
    die();
}
if (stripos($_SERVER["HTTP_USER_AGENT" ], "Google Page Speed Insights") && !stripos("HTTP_USER_AGENT" ], "Mobile")) {
    echo $p_0;
    die();
}

В коде проверяются заголовки User Agent’а: если заходит бот Google, либо PR-CY.RU, то отдаем им «упрощенную версию сайта», либо мобильную, либо десктопную — пустой HTML файл с одним изображением скриншота сайта.

В переменных $p_0 и $p_1 лежит текст с HTML-страницей примерно со следующим содержимым:

<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgo=" type="image/x-icon"> 
<title>Pagespeed</title>
<meta charset="utf-8">
<meta name="keywords" content="Pages speed">
<meta name="description" content="Pages speed">
<meta name="viewport" content="initial-scale=1, width=device-width">
</head>
<body>
<style>*{padding:0;margin:0} img{animation: fadein 1s;} @keyframes fadein { from { opacity: 0; } to { opacity: 1; }}</style>

<img src="data:image/jpeg;base64,

Полностью код с содержимым base64 изображения не выкладываю в целях конфиденциальности. Но я думаю общий смысл понятен для тех кто разбирается.

Скрипт проверяет кто заходит на сайт. Если это бот Google или PR-CY.RU, то им заместо сайта отдается скриншот сайта. Из-за небольшого объёма страницы и быстрой загрузки баллы увеличиваются до 100.

Кстати, обратите внимание на CSS в HTML. Ребята действительно «постарались» и сделали так, чтобы скриншот загружался плавно, с анимацией прозрачности. Думаю это для того, чтобы заказчики ничего не заподозрили глядя в скриншоты trace рендеринга страницы, а может такая обманка для Google. Не ясно до конца, но красиво.

Всё было бы смешно если бы не было так грустно для клиентов. Сайт вполне может попасть под блокировку за обман, а деньги за работы уже ушли.

Вот такие вот интересные IT-кадры попадаются, коллегами назвать язык не поворачивается. Полное неуважение к окружающим и к себе в первую очередь.

Доработка поиска Joomshopping’а — добавляем изображения в результаты поиска

Для Joomshopping существует официальный поисковый плагин, позволяющий с помощью com_search компонента джумлы находить наши товары. Но есть в этом плагине недостатки:

  1. Ищет он только по товарам. Поиск по категориям отсутствует
  2. Хотелось бы в результатах поиска видеть изображение искомого товара

Первый пункт легко дорабатывается дополнительным запросом в базу. А вот со вторым пришлось немного изменить стандартные шаблоны вывода поиска.

После установки поискового плагина Joomshopping его можно найти по сл. пути: /plugins/search/joomshopping/joomshopping.php

Сначала я дополнил в нем стандартный запрос. Добавил в SELECT поле image_name и через LEFT JOIN добавил таблицу с изображениями товаров.

Далее я прошелся по результатам этого запроса и сразу исправил ссылку на товар и заместо названия файла картинки прописал полный путь к ней:

Остается только добавить вывод картинки в шаблоне поиска. Шаблон поиска конечно лучше переопределить в папке своего шаблона. Для этого нужно создать в шаблоне папку html, создать в ней папку com_search, а в ней еще одну папку search. В конечную папку скопировать файлы из папки /components/com_search/views/search/tmpl/default.php.

В итоге структура файлов должна быть примерно такой:

Результаты поиска формируются в файле default_results.php, выводим изображение:

Немного исправив шаблоны com_search можно добиться отличного оформления вывода поиска, что особенно актуально для интернет-магазинов:

2016-04-20_01-17-13

Кому интересно, выкладываю архив со всеми изменениями. Учтите, что решение может не заработать на вашей конфигурации, возможно придется кое-что поменять. В частности, на другом сайте с Joomshopping мне пришлось поменять названия полей в запросе, т.к. таблицы отличались. Возможно это были разные версии Joomshopping’а.

Скачать: joomshopping-search-images.zip

iMacros скрипт для добавления пользователей из группы в свою группу Одноклассников

Написал небольшой iMacros скрипт для добавление пользователей ok.ru в группы со страниц других групп. Работает в Firefox версии iMacros. Возможно кому-нибудь пригодится.

 

Как работают «оптимизаторы» в «SEO-компании №1» или небольшой отзыв на Demis Group

Есть у меня товарищ с сайтом на Joomla, который я сделал год назад. Т.к. я не занимался и не занимаюсь продвижением сайтов, на вопрос о повышении позиций в поисковиках, порекомендовал товарищу самостоятельно найти компанию, которая профессионально этим занимается.

logo

Такая компания нашлась, называется она Demis Group. Предоставили им доступы и начали работу. Тут стоит отметить, что все это время ко мне не обращались с правками, поэтому я не застал процесс «оптимизации», и очень зря, как оказалось.

Проблемы обнаружились, когда меня попросили сделать адаптивную версию сайта. Клиенту оптимизатор посоветовал, мол, Гуглу нравятся такие сайты, они выше в выдаче, да и в целом удобнее. Сайт тогда мы сделали фиксированным, был ограничен бюджет. Ну не проблема, сказано — сделано!

Открываю сайт, смотрю внешне, в админке. Параллельно запускаю PhpStorm, гружу проект к себе на компьютер с SFTP, ведь были изменения со стороны оптимизатора. Смотрю на тайтлы, как были при мне, такими же и остались. WTF? Где продвижение? В админке, в пунктах меню все тоже самое. Никаких компонентов SEO не установлено. Ооокей, смотрю корень сайта и нахожу чужеродные файлы:

Думаю, некоторые по названию уже начали понимать, с чем мне пришлось столкнуться.

_adminer.php — это менеджер БД, взят отсюда http://www.adminer.org/
_varvara.php — самописная хрень, которая делает поиск и замену во всех файлах на хостинге

Еще раз, Демису были предоставлены все доступы к хостингу. Что мешало воспользоваться встроенным у хостера PhpMyAdmin? Зачем устанавливать потенциально опасные скрипты, без спроса, какая в этом необходимость? Абсолютно непрофессиональный подход к делу, даже опасный.

Ок, смотрим файлы d-seo.php.

 

Великолепно, Demis! Заместо того, чтобы воспользоваться встроенными SEO функциями Joomla, мы сделаем свой велосипед!

velosiped-sdelay-samЧто, если клиент поменяет алиас URL у пункта меню? Ничего страшного, мы ведь об этом не узнаем, да и клиент не сразу догадается об отвалившихся тайтлах, а если и узнает, ну так что стоит зайти и исправить? Ну да, клиент сам не полезет в этот ужас, для этого ведь существуем мы!

Теперь файл d-url-rewriter.php

 

Я надеюсь, после такой жести у PHP-программистов глаза не вытекли, но кровоточат однозначно. Интересно было бы узнать, есть ли тут уязвимости? Если вы отлично шарите в PHP, напишите в комментах, пожалуйста, что нашли, да и в целом про код свои комментарии интересны.

Меня лично заинтересовала эта строчка:

Судя по всему, Demis Group использует данное решение не только с Joomla, но и в других популярных CMS, таких как Bitrix.

Но страшно не это. Страшно то, что заместо того, чтобы поменять заголовки в материалах или в шаблонах, Demis Group пишет ультра-костыль, который автоматически находит все найденные заголовки и обрабатывает их через свои preg_replace с жуткими регэкпами.

Далее, Demis Group проявила профессионализм, написав ВРУЧНУЮ файл sitemap.xml. Это при том, что в админке уже существовал компонент, который генерирует sitemap.xml автоматически из существующих пунктов меню. Интересно, оптимизатор заглядывал в админку?

Немного успокоившись от увиденного, я начал делать адаптивную версию. Но и тут меня ждал сюрприз. Дело в том, что я использую LESS-препроцессор для генерации CSS. Потому, что удобно и быстро. Догадываетесь, о чем я? Интуиция и тут не подвела. Бэкаплю текущий template.css, затем генерирую новый через LESS, делаю DIFF и что я вижу?

«Оптимизатор» проявил чудеса усердия! Он вставил переносы между каждыми правилами, после закрывающихся фигурных скобок. Честно, мне даже стало немного его жалко.

2015-05-15 13-15-13 Clipboard vs  Applications MAMP htdocs mtk-met.ru templates mtk delete-css template.css_

Естественно «оптимизатор» не знал, что такое normalize.css, поэтому свои правила он вставлял и выше и ниже и в сам normalize, который был импортирован при компиляции. При этом не обошлось без ошибок (подчеркнуты красным). Удивительно, но все браузеры переварили без проблем. 2015-05-15 13-13-12 template.css - mtk-met.ru - [ Applications MAMP htdocs mtk-met.ru]

На просьбы поменять все это безобразие, Demis Group всего лишь переподключили свои скрипты и они как-то заработали. На резонное замечание о костылях, Demis ответили, что скрипты нужны и что они выполняют важную задачу генерации заголовков. Что же, с этим сложно спорить. Все мои претензии в итоге оказались монологом со стеной. Demis Group, насколько я понял, не выгодно делать оптимизацию хорошо.

Какие выводы можно сделать из всего этого? Для себя лично я вынес вот что:

1) Если вы ведете проект как разработчик, но не занимаетесь продвижением, то имеет смысл пристально наблюдать за тем, как ведется продвижение, чтобы не столкнуться с описанным выше.

2) Всю компиляцию я делаю в Grunt, там же, отныне, использую minify для сжатия CSS и JS-файлов. Это немного уменьшит шанс того, что идиот начнет править файлы (хотя, мне попадались и такие).

UPD: Спустя пару недель, после того, как я переделал все CSS файлы в сжатые, Demis Group не постеснялись и их поменять. :/ Еще и CSS файлы, которые компилируются удалили. Действильно, зачем они нужны, если есть минифицированный? Клинический случай.

3) Рейтинг SEO компаний — пустой треп. Чем больше клиентов, тем больше текучка, качество на нуле, ответственность тоже. Я об этом догадывался, но теперь на личном примере убедился.

2015-05-15 13-52-27 ПРОДВИЖЕНИЕ САЙТОВ

2015-05-15 13-49-03 Seo-компания Demis Group

Если вы видите, что компания на своем сайте навязчиво начинает трясти своими «дипломами», «рейтингами» и прочей ахинеей, то это повод серьезно задуматься.

16 гигабайт памяти в HP Probook 4540s

Столкнулся с дурацкой проблемой. Официально модель HP Probook 4540s заявлена поддержка до 12Гб оперативной памяти. Я же, по рекомендации сайта Corsair купил 16Гб. Никаких проблем в Mac OS X не возникло и память определилась и все работало отлично. Проблема была с установкой Windows. Сам установщик запускался 30-40 минут, а установка зависала. Перепробовал все версии Windows 7, 8, 8.1. Грешил на память. Выткнул одну планку и… о чудо, все заработало и установка прошла моментально! В чем же дело? Прошел Memtest, никаких ошибок.

corsair_so_dimm_ddr3_8gb_2x4gb_1333mhz_pc_10600_cmso8gx3m2a1333c9__1

Начал просматривать форумы оф. поддержки HP. На 20 страницах обсуждения подобной проблемы наконец нашел решение:

1) Сначала вставить одну планку на 8GB.
2) После установки Windows или, если она у вас уже установлена, нужно запустить

msconfig — Загрузка — Дополнительно. Поставить максимум памяти в значение 8192 MB
3) Перезагрузиться, затем выключить компьтер, вставить вторую планку. Включить. Windows сейчас использует 8гб, поэтому загрузится все быстро.
4) Опять идем в msconfig — Загрузка — Дополнительно. И теперь устанавливаем максимум памяти в значение 12032 (если у вас 8GB+4GB планки) или 16128 (если у вас две планки по 8GB).