Давайте начнем с того, что же такое AMP (Accelerated Mobile Pages). В интернете пишут, что это «опен‑сорс HTML фреймворк». Лично меня сбивает с толку это определение. В моем понимании AMP — это скорее платформа под эгидой Google, которая предоставляет создателям сайтов инструменты и инфраструктуру для улучшения перформанса, накладывая при этом ограничения на функционал. Что (из основного) делает гугл с помощью AMP: Ускоряет загрузку контента за счет размещения всей статики (HTML, картинки, видео) на кеширующих CDN, а также за счет минификации, компрессии и других техник оптимизации WEB контента. Улучшает перформанс интерфейсов за счет запрета на встраивание JS, агрессивных анимаций, айфреймов и прочего. Предоставляет инструменты для встраивания различных виджетов (реклама, аудио, видео, галерейки, сторис,...). Несмотря на все «запреты», AMP действительно дает значительный буст в перформансе сайта. Он срезает косты на собственные CDN, ускоряет загрузку и увеличивает глубину просмотра. Но самое главное для нас — несмотря на все ограничения, AMP позволяет встраивать на «ускоренные» сайты «старую добрую» рекламу. Как уже было сказано выше, AMP позволяет встраивать на сайты разные «виджеты». Один из таких виджетов — amp‑ad. Пример пустой странички с рекламой на основе APM можно посмотреть тут. Давайте разберем, этот пример: HTML в AMP — самый обычный HTML документ с некоторыми особенностями. Описание базовых тегов можно найти в документации. Нас же интересует все, что связано с рекламой. В head мы добавили скрипт для поддержки кастомного элемента amp-ad После этого вставляем саму рекламу в тело страницы <div placeholder/> решает понятную задачу — показать «что‑нибудь», пока не подгрузилась реклама. width и height — обязательные параметры, которые помогут зафиксировать размеры и тем самым избежать увеличения Cumulative Layout Shift type="industrybrains" — идентификатор рекламной сети, а data-width, data-height, data-cid — параметры рекламы, которые будут переданы напрямую в рекламную сеть. Очевидно, что представленных параметров недостаточно для того, чтобы сделать запрос за рекламой. Нужен как минимум адрес, по которому реклама грузится. Тут то мы и остановимся поподробнее. На месте рекламного элемента библиотека AMP вставляет безкуковый iframe с адресом типа https://d-123.ampproject.net/123/frame.html и содержимым типа такого. В этот iframe через атрибут name передается строкой вот такой объект (помним, что даже изнутри секьюрного фрейма можно прочитать атрибут name): Внутри iframe через document.write вставляется скрипт рекламной сети. В нашем примере мы используем рекламную сеть industrybrains, они подключаются «в лоб» — сразу грузится скрипт рекламной сети (из поля bootstrap объекта выше) https://3p.ampproject.net/2310271806000/vendor/industrybrains.js. Адрес скрипта рекламной сети зашит прямо в код компонента рекламы (https://cdn.ampproject.org/v0/amp-ad-0.1.js). После этого скрипт самостоятельно вычитывает window.name со всеми параметрами и на основе них рисует рекламу. Более того, скрипт сам определяет глобальную функцию draw3p, которая вызывается внутри фрейма кодом вставки. Это не совсем «правильная» интеграция, потому что слишком много контроля отдано на сторону рекламной сети. Если следовать стандартной инструкции, все делается немного по‑другому. Давайте посмотрим на примере того, как в AMP встроена реклама Яндекса. В репозитории AMP лежит общая конфигурация всех рекламных сетей (параметры работы и данные для предзагрузки). Эта конфигурация приезжает на страницу вместе со скриптом виджета рекламы https://cdn.ampproject.org/v0/amp-ad-0.1.js. Скрипт Яндекса (в отличие от industrybrains), не грузится напрямую в айфрейме. Вместо этого загружается скрипт https://3p.ampproject.net/2310301456000/vendor/yandex.js. Этот скрипт содержит в себе: Код вставки Яндекса в AMP (в него будут переданы параметры из data‑атрибутов) Дополнительный функционал, необходимый для работы рекламы в контексте AMP (глобальный объект context и его методы) При регистрации все рекламные сети должны соблюсти несколько ритуалов разной степени обязательности. Например, добавить документацию по использованию и пример кода вставки. После того как все сделано и проросло в новую версию AMP — можно использовать рекламу у себя на сайте. Все, что описано выше, описывает 3p (third‑party интеграцию, которая в рамках AMP считается «не крутой и медленной». Ниже будет более подробный разбор дополнительных, более «хайповых» рекламных «фишек» AMP. Внутри iframe у AMP не так много власти, поэтому контролировать перформанс самой рекламы сложнее (или невозможно). По факту рекламные системы внутри своих айфреймов могут воровать и убивать делать все, что захотят. Чтобы решить и эту проблему — был придуман стандарт AMPHTML ads, он же a4a (AMP for ads), от же Fast Fetch, ускоряющий рекламу на AMP страницах. Принцип следующий — если твое рекламное объявление соответствует «стандарту» и прошло «валидацию» — вставим ее прямо на AMP страницу во friendly iframe без лишних вопросов. В этом случае реклама рисуется быстро, перформит хорошо, пользователи, рекламодатели и паблишеры счастливы, рекламная система зарабатывает много денег. Если же с объявлением проблемы — рисуем его в «old‑school, legacy, slow, sandboxed iframe». Для сравнения a4a и 3p рекламы, можно воспользоваться страничкой (из России у меня не работает, но под VPN норм). Упрощенный пример a4a тут. Этот пример основан на коде Doubleclick, исходники которого лежат тут. Видно, что a4a реклама рисуется во friendly iframe. Во время отрисовки грузится скрипт https://cdn.ampproject.org/rtv/012310301456000/v0/amp-ad-network-doubleclick-impl-0.1.js, который собирается из репозитория AMP. Внутрь этого скрипта, помимо прочего, зашит метод getAdUrl, который генерирует ссылку для загрузки рекламы. Сам запрос делается скриптом https://cdn.ampproject.org/v0.js через fetch(credentials=include, method=GET, mode=cors). Пример ответа от Doubleclick. В ответе от Doubleclick также фигурирует заголовок Amp-Fast-Fetch-Signature: google:1:NU123xfhbw==. Он используется для проверки на соответствие рекламы стандартам a4a. Для этого внутри рекламной сети после подбора рекламы делается дополнительный запрос на централизованный сервер валидации с итоговым кодом рекламного баннера. Если все хорошо — сервер валидации возвращает «электронную подпись» для этого баннера, которая и передается на клиент в заголовке Amp-Fast-Fetch-Signature. Валидация на клиенте происходит с помощью Web Crypto. Если браузер не поддерживает Web Crypto, либо рекламная система не прошла все ритуалы интеграции, либо что‑то еще идет не по плану, реклама рисуется в third‑party фрейме (в режиме legacy). Если реклама не пришла совсем — рекламный элемент удаляется со страницы. В ответе можно увидеть тег amp‑analytics. Как можно догадаться из названия и содержания, тег позволяет встраивать дополнительные трекинги. Они бывают двух типов: 1) с использованием вендоров (прошедших все необходимые ритуалы), 2) либо трекинги в режиме «in‑house». Вендоры коммитят свои конфиги пикселей прямо в репозиторий AMP, откуда они прорастают в скрипт https://cdn.ampproject.org/v0/amp-analytics-0.1.js. Пример трекинга с использованием вендора можно найти тут. В нашем же примере используется in‑house решение, в котором можно прописать кастомные трекинги и условия их срабатывания. amp‑pixel — еще одна «фича» для трекинга, но с меньшим количеством функционала. Отдельно нужно упомянуть про RTC (Real‑Time Config). Это еще одна «технология» в рамках Fast Fetch, которая позволяет обогащать рекламные запросы. У элемента amp-ad появляется дополнительный атрибут rtc-config. В конфиге прописывается список вендоров (с параметрами) и/или ссылок для запроса за дополнительными данными о пользователях. Список всех доступных вендоров и поддерживаемых ими параметров можно найти тут. RTC позволяет для каждого рекламного места устанавливать до 5 «ручек». Пример такого элемента. Вендоры в ответ на XHR запрос возвращают JSON объект с параметрами. В уже известную нам функцию getAdUrl вторым аргументом прилетает промис, который резолвится с массивом этих параметров. Можно дождаться резолва этого промиса и подставить параметры в ссылку.