Реалізація вбудовування контенту (Embed) з сайту
Зламаний інтерфейс редактора, коли вставка YouTube-відео перетворюється на головний біль через несумісність iframe і CSP — знайома ситуація? Неоптимізовані embed-віджети збільшують LCP на 3-5 секунд, а помилки CSP блокують близько 20% віджетів, знижуючи конверсію. Ми вирішуємо цю проблему системно: проектуємо embed-шар, який безпечний, адаптивний і не гальмує завантаження. Наша команда має 5+ років досвіду в розробці embed-рішень для медіасайтів та освітніх платформ, виконавши понад 50 проєктів. Гарантуємо безпеку та продуктивність під навантаженням. Економія часу розробки становить до 70%, що дозволяє знизити бюджет проєкту на 20-30% порівняно з ручною інтеграцією кожного провайдера.
Проблеми, які ми вирішуємо
Стандартний iframe з атрибутом sandbox без allow-same-origin ламає багато плеєрів — YouTube вимагає allow-scripts + allow-same-origin. CSP блокує iframe з ненадійних джерел, якщо не додати frame-src. Фіксовані розміри oEmbed-коду призводять до переповнення на мобільних. Синхронне завантаження скриптів плеєра збільшує LCP на 2–3 секунди. Відсутність кешування змушує щоразу смикати API провайдера, збільшуючи TTFB. Також відсутність аналітики переглядів не дозволяє відстежувати залученість користувачів — виправляємо це вбудовуванням кастомних подій.
Як працює oEmbed: порівняння підходів
| Підхід | Простота | Безпека | Адаптивність | Продуктивність |
|---|---|---|---|---|
| Прямий iframe | Висока | Низька (XSS, clickjacking) | Ні (фіксовані розміри) | Низька (синхронне завантаження) |
| oEmbed + DOMPurify | Середня | Висока (санітація, CSP) | Так (CSS aspect-ratio) | Середня (lazy load) |
| Кастомний embed (Figma) | Низька | Висока (ручний контроль) | Так | Висока (статика) |
oEmbed дає баланс: автоматичне отримання HTML, безпеку через санітацію та адаптивність через постобробку. Специфікація oEmbed описує JSON-протокол, який підтримують десятки платформ — покриття 95% популярних сервісів.
Реалізація oEmbed-проксі в 3 кроки
Крок 1: Проксі-контролер на Laravel
class OEmbedController extends Controller
{
private array $providers = [
'youtube.com' => 'https://www.youtube.com/oembed',
'youtu.be' => 'https://www.youtube.com/oembed',
'vimeo.com' => 'https://vimeo.com/api/oembed.json',
'twitter.com' => 'https://publish.twitter.com/oembed',
'x.com' => 'https://publish.twitter.com/oembed',
'instagram.com' => 'https://graph.facebook.com/v18.0/instagram_oembed',
'soundcloud.com' => 'https://soundcloud.com/oembed',
'spotify.com' => 'https://open.spotify.com/oembed',
'tiktok.com' => 'https://www.tiktok.com/oembed',
'codepen.io' => 'https://codepen.io/api/oembed',
];
public function fetch(Request $request): JsonResponse
{
$url = $request->validate(['url' => 'required|url'])['url'];
$host = preg_replace('/^www\./', '', parse_url($url, PHP_URL_HOST));
$endpoint = collect($this->providers)
->first(fn($v, $k) => str_contains($host, $k));
if (!$endpoint) {
return response()->json(['error' => 'Provider not supported'], 422);
}
$response = Http::timeout(5)->get($endpoint, [
'url' => $url,
'maxwidth' => $request->integer('maxwidth', 800),
'format' => 'json',
]);
return response()->json($response->json(), $response->status());
}
}
Крок 2: Кешування та безпека
Кешуємо відповіді oEmbed на добу — embed-код оновлюється рідко. Для санітації використовуємо DOMPurify з whitelist-підходом: дозволяємо iframe лише від довірених доменів. CSP заголовки налаштовуємо під кожного провайдера. Це знижує навантаження на API провайдерів на 90%.
import DOMPurify from 'dompurify'
const ALLOWED_IFRAME_ORIGINS = [
'https://www.youtube.com',
'https://player.vimeo.com',
'https://open.spotify.com',
'https://w.soundcloud.com',
'https://www.tiktok.com',
'https://codepen.io',
]
DOMPurify.addHook('uponSanitizeElement', (node, data) => {
if (data.tagName === 'iframe') {
const src = node.getAttribute('src') || ''
const allowed = ALLOWED_IFRAME_ORIGINS.some(origin => src.startsWith(origin))
if (!allowed) node.remove()
}
})
const config = {
ADD_TAGS: ['iframe'],
ADD_ATTR: ['allowfullscreen', 'frameborder', 'scrolling', 'allow', 'referrerpolicy'],
}
function SafeEmbed({ html }: { html: string }) {
const clean = DOMPurify.sanitize(html, config)
return <div dangerouslySetInnerHTML={{ __html: clean }} className="embed-wrapper" />
}
Крок 3: Адаптивність та lazy loading
Видаляємо фіксовані розміри з HTML, обчислюємо aspect-ratio. Підключаємо IntersectionObserver для завантаження лише коли віджет з'являється в зоні видимості (із запасом 300px). Це знижує LCP на 40%.
.embed-wrapper iframe {
width: 100%;
border: none;
aspect-ratio: 16/9;
}
Чому oEmbed краще прямого iframe?
Прямий iframe — це копіпаста з документації, яка ламається при зміні пристрою або оновленні плеєра. oEmbed же дає єдиний інтерфейс для всіх провайдерів, автоматично оновлювану розмітку та можливість кешування. На практиці це скорочує час розробки інтеграції на 3–5 днів і знижує кількість багів на 70%.
Як захистити iframe від XSS? Практика CSP
Атрибут sandbox з allow-scripts і allow-same-origin — обов'язковий мінімум для більшості плеєрів. Але без CSP зловмисник може вставити iframe на сторонньому сайті та перехопити дані. В продакшені ми додаємо заголовок:
$response->headers->set('Content-Security-Policy',
"default-src 'self'; " .
"frame-src 'self' https://www.youtube.com https://player.vimeo.com; " .
"script-src 'self' 'nonce-...'"
);
Детальніше про політику безпеки контенту читайте в документації CSP.
Кастомні провайдери: Figma та GitHub
Для сервісів без oEmbed (наприклад, Figma) будуємо embed-код вручну. Figma надає документацію з параметрами. GitHub gist можна вбудувати через raw-скрипт. Покриття таких провайдерів розширює функціонал до 15+ джерел.
Процес роботи
- Аналітика: визначаємо список платформ, необхідних для вбудовування.
- Проєктування: архітектура проксі, безпечний рендер, кешування.
- Реалізація: пишемо проксі-контролер, санітацію, адаптивні стилі.
- Тестування: перевіряємо кожного провайдера, заміряємо LCP/CLS, пишемо юніт-тести.
- Деплой: налаштування CSP, моніторинг помилок, навантажувальне тестування.
Що входить в роботу
- Реалізація oEmbed-проксі з підтримкою 10+ провайдерів.
- Безпечний рендер через DOMPurify та налаштування CSP.
- Адаптивна верстка embed-віджетів (aspect-ratio, 100% ширина).
- Lazy loading через IntersectionObserver.
- Кешування відповідей oEmbed (Redis/Memcache).
- Документація та навчання команди.
- Підтримка після запуску (1 місяць).
Строки
Базова реалізація (проксі + 10 провайдерів) — від 2 днів. З адаптивом, lazy load та кешуванням — від 4 днів. Повноцінний редактор з WYSIWYG вставкою — від 1 тижня. Вартість розраховується індивідуально. Отримайте консультацію по вашому проєкту — оцінимо за 1 день. Зв'яжіться з нами, щоб обговорити деталі інтеграції та отримати кошторис. Такий підхід скорочує операційні витрати на 70% та прискорює time-to-market у 2 рази.
Таблиця підтримуваних провайдерів та їх latency
| Провайдер | Endpoint oEmbed | Середній час відповіді (мс) |
|---|---|---|
| YouTube | youtube.com/oembed | 120 |
| Vimeo | vimeo.com/api/oembed.json | 150 |
| publish.twitter.com/oembed | 180 | |
| graph.facebook.com/... | 200 | |
| SoundCloud | soundcloud.com/oembed | 160 |
| Spotify | open.spotify.com/oembed | 140 |
| TikTok | tiktok.com/oembed | 220 |
| Codepen | codepen.io/api/oembed | 100 |







