Как запретить Plausible фиксировать просмотр страницы при ошибке 404 в Laravel

Мне очень нравится использовать Plausible для аналитики. Google Analytics (дальше просто GA) был «немного» чересчур, особенно учитывая, что PageSpeed Insights каждый раз сообщает мне, что скрипт GA «слишком тяжёлый» (по иронии судьбы).

Одна из вещей, которая меня подкупила — их документация: короткая, простая, но эффективная. Особенно мне понравились руководства по установке и интеграции (в частности, там упоминается Laravel — мой основной инструмент, который в подобной документации нередко просто отсутствует).

Проблема с маршрутом

Для этого сайта я взял composer-пакет vincentbean/laravel-plausible, и интеграция прошла довольно гладко. Но, к сожалению, спустя месяц использования я заметил проблему — множество фиктивных запросов к несуществующим страницам и ресурсам. Это было вполне объяснимо, ведь один из маршрутов был wildcard-маршрутом вот такого вида:

Route::get('/{slug}', [PageController::class, 'page'])->where('slug', '^(?!.*\.|^\/(favicon|css|js|storage|build)).*$')->name('page');

Список спам-запросов

Когда этот список эндпоинтов появился у меня в дашборде, я подумал — не проблема, просто исключу их, и всё будет в порядке. Идея казалась вполне разумной... Но, боже, как я ошибался... Вот что ждало меня после того, как я добавил в исключения все основные эндпоинты:

Список эндпоинтов случайных российских банков

Увидев этот список эндпоинтов от каких-то российских банков, я понял, что фильтровать всё подряд — точно не мой путь. Поэтому, налив себе газировки со льдом, я принялся искать наименее болезненное решение проблемы с минимальными изменениями в коде.

Цели

Вот какое поведение я, по сути, ожидал получить:

  1. Отправлять событие, когда пакет настроен;
  2. Отправлять событие, когда страница существует;
  3. Отправлять событие, когда это не CLI (как бонус, потому что это тоже немного портило статистику);

Решение

Middleware в пакете довольно хорошо справлялся с пунктом 1, но пункты 2 и 3 оставались нерешёнными. Поэтому, изучив middleware, который, как и большинство middleware, оказался довольно пустым, я улучшил его код следующим образом:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
use VincentBean\Plausible\Facades\PlausibleEvent;

class TrackPlausiblePageviews
{
    public function handle(Request $request, Closure $next): mixed
    {
        $response = $next($request);

        if (config('plausible.tracking_domain') !== null 
						&& $response->status() !== Response::HTTP_NOT_FOUND 
						&& is_string($request->userAgent())
            && !in_array($request->userAgent(), config('plausible.ignore_agents'))) {
            PlausibleEvent::fire('pageview');
        }

        return $response;
    }

Итак, во-первых, я по-прежнему проверяю, настроен ли у меня tracking_domain, потому что если нет, то и обрабатывать событие дальше нет смысла. После этого я использую готовую функциональность Laravel — выброс 404 при использовании firstOrFail(). И, наконец, аккуратно проверяю User Agent, потому что иначе меня заспамливает мой же собственный генератор sitemap.

В config/plausible.php я просто добавил надоедливые агенты (и да, я захардкодил их, потому что они меняются не так уж часто). Так что не стесняйтесь добавить и этот вариант в свой конфиг:

	'ignore_agents' => [
        'GuzzleHttp/7'
    ],

Заключение

Полагаю, все знают фразу «Работает — не трогай». Что ж, в данном конкретном случае я с этим утверждением согласен. Моя проблема решилась всего парой строк кода, а моя газировка со льдом ещё даже не закончилась.

P.S. Команда Plausible — как мне удалить ЧАСТЬ старых записей в Analytics? 😅