Динамические метатеги для ботов и сканеров с помощью Firebase и Cloudflare Workers

Динамические метатеги для ботов и сканеров с помощью Firebase и Cloudflare Workers

От создателя: существует огромное количество методов предоставления динамических метатегов ботам и поисковым сканерам, большая часть из которых соединено с управлением вашими своими серверами, будь то SSR, подготовительный рендеринг либо создание статических веб-сайтов. Но что, если вы не желаете управлять сервером либо подключаться к платформе SSR, есть ли иной вариант?

Это ситуация, в какой я не так давно оказался.

Подготовительный рендеринг был бы неплохим решением; маршрутизировать входящие запросы на базе пользовательского агента, юзеры получают веб-сайт из CDN, а боты могут быть перенаправлены на службу подготовительного рендеринга. Сначала пасмурная функция либо лямбда кажется неплохим методом реализации этого, мы можем обработать пользовательские входящие запросы и подходящим образом навести их. Но есть соглашение с бессерверным режимом, и в этом случае это прохладный пуск. Если вы новичок в бессерверном режиме, прохладный пуск — это когда платформа замедляет ваш код, когда он не употребляется; это произойдет, если ваша рабочая перегрузка непостоянна. Неувязка возникает, когда поступает новейший запрос, и платформе нужно опять загрузить и инициализировать код, что происходит медлительно. При прохладном запуске юзеры могут ожидать несколько секунд (5 либо наиболее), до этого чем сервер будет готов обработать входящий запрос, что в этом случае неприемлемо.

AWS дает так именуемый «Provisioned Concurrency» для Lambda Functions, чтоб уменьшить прохладный пуск. На самом деле, вы сможете заплатить за то, чтоб некие функции оставались «теплыми» 24 часа в день, 7 дней в недельку, но для меня это лишает смысла бессерверность, не так ли? Преимущество оплаты лишь за то, что вы используете, и возможность моментального масштабирования в согласовании со спросом, сейчас пропали.

Ни о каком прохладном старте не быть может и речи, поэтому что он очень неспешный. Перебегайте на Cloudflare Workers. Cloudflare Workers различаются от бессерверных предложений GCP и AWS, но у их также есть и для иная цель. Они не запускают Node и не раскручивают виртуальную машинку, потому это дозволяет Cloudflare иметь объявленное время прохладного пуска 0 мс при развертывании в 155 местах в Cloudflare CDN. Это смягчает две препядствия, которые у меня появляются при использовании пасмурных функций / лямбда для таковых целей, как маршрутизация входящих запросов.

У воркеров есть некие ограничения, к примеру, на уровне бесплатного использования вы получаете лишь 10 мсек за один вызов. Но можно просто применять Worker в качестве оборотного прокси, чтоб проверить, как пользовательский агент идентифицирует себя и на базе этого будет показывать правильные представления. Итак, давайте создадим это.

READ
Новая стратегия популяризации поискового продвижения от Google

Заместо того, чтоб добавлять домен в Firebase Hosting, я добавил его в Cloudflare. Я использую Cloudflare в качестве DNS и перенаправляю запросы через Worker, который действует как оборотный прокси.

JavaScript

12345678910111213141516171819202122232425262728293031323334353637383940414243444546const userAgents = [  “googlebot”,  “Yahoo! Slurp”,  “bingbot”,  “yandex”,  “baiduspider”,  “facebookexternalhit”,  “twitterbot”,  “rogerbot”,  “linkedinbot”,  “embedly”,  “quora link preview”,  “showyoubot”,  “outbrain”,  “pinterest/0.”,  “developers.google.com/+/web/snippet”,  “slackbot”,  “vkShare”,  “W3C_Validator”,  “redditbot”,  “Applebot”,  “WhatsApp”,  “flipboard”,  “tumblr”,  “bitlybot”,  “SkypeUriPreview”,  “nuzzel”,  “Discordbot”,  “Google Page Speed”,  “Qwantify”,  “pinterestbot”,  “Bitrix link preview”,  “XING-contenttabreceiver”,  “Chrome-Lighthouse”,]; /** * Detect whether the user agent string matches that of a known bot * @param {string} userAgent */export default (userAgent) => {  return userAgents.some(    (crawlerUserAgent) =>      userAgent.toLowerCase().indexOf(crawlerUserAgent.toLowerCase()) !== -1  );};

JavaScript

12345678910111213141516171819202122232425262728293031323334353637import isBot from “./isBot”; const publicDomain = “your-domain.com”;const upstreamDomain = “your-project.web.app”;const prerenderEndpoint =  “https://us-central1-your-project.cloudfunctions.net/prerender”; /** * Handles the incoming request * @param {Request} request */async function handleRequest(request) {  const { method, headers, url } = request;  const userAgent = request.headers.get(“user-agent”);   let fetchUrl = “”;   // Set the fetch URL based on the result of isBot  if (isBot(userAgent)) {    // Extract the path segment of the domain and append it as a query param to    // the upstream prerender endpoint    const path = url.split(publicDomain).pop();    fetchUrl = `${prerenderEndpoint}?path=${encodeURI(path)}`;  } else {    // replace the publicDomain with the upstreamDomain    fetchUrl = url.replace(publicDomain, upstreamDomain);  }   return fetch(fetchUrl, {    method,    headers,  });} addEventListener(“fetch”, (event) => {  event.respondWith(handleRequest(event.request));});
READ
Несколько фонов в CSS

Массив строк пользовательского агента и условное выражение взяты из пакета prerender-node, который является промежным программным обеспечением экспресс-обработки с целью проверки, является ли пользовательский агент ботом для маршрутизации входящих запросов в службу подготовительной визуализации.

Это работает весьма отлично, входящие запросы от юзеров весьма стремительно обрабатываются из Cloudflare / Гугл CDN.

2-ая часть — настроить пред-рендерер. Мы могли бы выслать его в службу вроде prerender.io, но пасмурные функции поддерживают Puppeteer прямо из коробки, и это еще не конец света, если бот должен ожидать прохладного пуска, пока он не истечет. Puppeteer может за ранее отрисовать нашу страничку и возвратить строчку HTML.

JavaScript

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485import { db, functions } from “@/admin.config”; const upstreamDomain = “your-project.web.app”; const cacheDurationSeconds = 86400;const cacheDurationMilliSeconds = cacheDurationSeconds * 1000; const prerenderFunction = async (  req: functions.https.Request,  res: functions.Response<string>): Promise<void> => {  const chromium = (await import(“chrome-aws-lambda”)).default;   const browserPromise = chromium.puppeteer.launch({    args: chromium.args,    defaultViewport: chromium.defaultViewport,    executablePath: await chromium.executablePath,    headless: chromium.headless,    ignoreHTTPSErrors: true,  });   const prerenderCacheReference = db.collection(“prerenderCache”);   // Decode the path from the query  const encodedPath = req.query.path as string;  const path = decodeURI(encodedPath);   // Check if the requested page exists in the cache  const prerenderCachePathQuerySnapshot = await prerenderCacheReference    .where(“path”, “==”, path)    .get();   // If the page is in the cache  if (!prerenderCachePathQuerySnapshot.empty) {    const { html, date } = prerenderCachePathQuerySnapshot.docs[0].data();     // Check cache age    const cacheAge = Date.now() – date;     if (cacheAge <= cacheDurationMilliSeconds) {      // Send cached HTML back      res.status(200).send(html);       const browser = await browserPromise;      await browser.close();       return;    }     // Else remove the page from cache and continue execution    const { id } = prerenderCachePathQuerySnapshot.docs[0];    prerenderCacheReference.doc(id).delete();  }   // Prerender the page   const browser = await browserPromise;  const page = await browser.newPage();   await page.goto(`https://${upstreamDomain}${path}`, {    waitUntil: “networkidle2”,  });   const html = await page.content(); // serialize HTML  const pageClosePromise = page.close();   // Add the new page to the cache  const pageCachePromise = prerenderCacheReference.add({    html,    path,    date: Date.now(),  });   await Promise.all([pageClosePromise, pageCachePromise]);   res.status(200).send(html);  return;}; export const prerender = functions  .runWith({ memory: “2GB” })  .https.onRequest(async (req, res) => {    res.set(“Access-Control-Allow-Origin”, “*”);    await prerenderFunction(req, res);  });
READ
Google My Business поменял отношение к компаниям в статусе temporarily closed

Функция открывает новейшую вкладку в Chrome без заголовка, делает запрос к Firebase Hosting, показывает приобретенный HTML и закрывает вкладку. Чтоб быть незначительно хитрее, я кэширую любой запрос в Firestore, чтоб убыстрить ответ, когда что-то уже было за ранее обработано. Срок деяния кеша в истинное время установлен на 1 денек, вы также сможете программно очищать маршруты в кеше при обновлении содержимого, независимо от варианта использования.

Как это работает? Что ж, по сути это работает весьма отлично, жаркие запросы к функции занимают всего 1,5 секунды, когда страничка не кешируется, что достаточно уместно. В худших вариантах я лицезрел, что запросы занимают до 8 секунд, что не весьма отлично, если они кешируются, но в моих ненаучных тестах не истекло время ожидания ни 1-го из ботов. Чтоб еще более сделать лучше время отклика, я вызываю функцию, когда кто-то надавливает клавишу «Поделиться» на моем веб-сайте, это подогревает функцию, также кэширует эту страничку до того, как она будет просканирована, потому эти запросы достаточно эффективны.

В конечном счете, я думаю, что это решение работает весьма отлично для моего варианта использования, если вы не сможете смириться с прохладным пуском для ботов, может быть, вы возжелаете навести свои запросы на службу типа prerender.io. Либо, может быть, для вас вправду необходимо управлять своим сервером.

Создатель: Richard Cooke

Редакция: Команда webformyself.

Источник

Оценить статью
Блог о самом интересном.
Добавить комментарий