Backend for Frontend: слой, который знает, кто спрашивает
Паттерн описали в 2015 году ради скорости и автономии команд. Через одиннадцать лет черновик IETF стал настойчиво рекомендовать его ради того, о чём его авторы не писали, — чтобы токен вообще не попадал в браузер.
Полное техническое изложение
TL;DR
- BFF — это маленький сервер, который стоит между вашим экраном и остальными сервисами и работает на этот конкретный экран.
- Первая причина его завести: собрать данные для экрана одним обращением вместо шести. Каждое обращение из браузера стоит времени, и эти времена складываются.
- Вторая причина, появившаяся намного позже: ключ от сессии остаётся на сервере и в браузер вообще не попадает.
- Но от взлома самой страницы BFF не спасает. Он решает, что злоумышленник унесёт с собой, а не сможет ли он навредить прямо сейчас.
Задача, из которой всё выросло
Представьте экран профиля. Чтобы его нарисовать, нужны сами данные пользователя, его закладки, прогресс чтения и ещё пара мелочей. Если браузер спрашивает это по частям, он делает несколько отдельных обращений к серверу — и ждёт ответа на каждое.
Пока вы разрабатываете на своём ноутбуке, ожидание почти нулевое: сервер стоит на той же машине. В реальной сети всё иначе, особенно на мобильном интернете. И вот тут выясняется неприятное: время не «примерно одинаковое», а складывается.
Сколько это стоит на самом деле
Ниже — измерение на настоящем приложении. Задержка в 150 мс на каждый запрос наложена специально: без неё измерение показало бы, что сеть бесплатна, а это и есть та самая иллюзия, которую нужно разрушить.
6 запросов из браузера, последовательно
900 мс
6 × 150 мс. Задержка платится столько раз, сколько сделано обращений.
один запрос к BFF, 6 — внутри
154.3 мс
150 мс + 6 × 0.71 мс. Второе слагаемое — измеренная медиана одного обращения к Go-сервису с той же машины.
Один запрос — 165 мс. Шесть запросов — 972 мс. Почти секунда, за которую сервер не сделал ничего сложного: браузер просто шесть раз сходил туда и обратно.
Идея BFF в одном предложении: пусть браузер сделает одно обращение, а все шесть внутренних вызовов сделает сервер, которому до соседних сервисов рукой подать. В том же измерении обращение к соседнему сервису заняло меньше миллисекунды.
Важная оговорка: если ваш экран собирается одним запросом, собирать нечего и BFF по этой причине не нужен — вы добавите лишний шаг и ничего не выиграете.
Вторая причина: ключ от двери
Есть второй повод завести такой слой, и он совсем о другом.
Обычное приложение, которое ходит в API само, вынуждено где-то хранить ключ от сессии — токен. Хранить его приходится там, куда дотягивается JavaScript: в localStorage или в обычной куке. А значит, туда же дотянется и чужой скрипт, если его удастся подсунуть на страницу.
BFF позволяет этого не делать. Браузер получает не токен, а куку с пометкой HttpOnly — такую, которую JavaScript прочитать не может в принципе. Настоящий токен остаётся на сервере, и сервер сам подставляет его в запросы дальше.
Вот измерение того, что видит скрипт при трёх способах хранения. Переключайте пробы кнопками.
> localStorage.getItem("access_token")
Токен в localStorage
SPA ходит в API сама
утечка
"tok_1f3a9c8e2b7d4a06"
Тот самый токен, который был туда положен.
Токен в куке без HttpOnly
SPA ходит в API сама
недоступно
null
В localStorage его нет — он в куке.
Сессия в HttpOnly-куке за BFF
браузер знает только BFF
недоступно
null
Измерено: localStorage приложения пуст, ключей нет вообще.
HttpOnly. Обратите внимание на третью пробу: колонка «за BFF» отличается от остальных только тем, что у скрипта нет токена, — данные он получает всё равно.А теперь неприятная правда
Переключите визуализацию на третью пробу — ту, где скрипт запрашивает закладки.
Он их получает. Токена у него нет, а данные есть.
Причина простая: скрипт выполняется на вашей же странице, а значит, для браузера он — это вы. Браузер послушно прикладывает куку к его запросам, как приложил бы к вашим.
Отсюда правило, которое стоит запомнить именно в такой формулировке:
BFF меняет не вероятность взлома, а его последствия. Украсть ключ и уйти с ним — больше нельзя. Пошалить от вашего имени, пока открыта вкладка, — по-прежнему можно.
Разница здесь не косметическая. Украденный токен работает и после того, как вы закрыли вкладку, из любой точки мира и без вашего участия. Доступ «пока открыта вкладка» заканчивается вместе с вкладкой.
Что ещё обязательно, если сессия живёт в куке
Черновик стандарта IETF, описывающий этот подход, требует трёх вещей, и ни одна из них не опциональна в реальном приложении:
- кука ходит только по защищённому соединению;
- кука недоступна для JavaScript;
- отдельная защита от подделки запросов — само по себе свойство куки от этого не спасает.
Последний пункт часто пропускают. Существует настройка SameSite, которая просит браузер не прикладывать куку к запросам с чужих сайтов, — и её нередко считают достаточной. Спецификация куки говорит обратным текстом: это полезный дополнительный слой, но не полноценная защита. Поэтому сервер дополнительно выдаёт одноразовый признак — токен, который знает только ваша страница, — и проверяет его на каждом действии, что-то меняющем.
Проверить это можно прямо в измерении выше: запрос с валидной кукой, но без такого токена получает отказ.
Когда BFF не нужен
Коротко: когда нечего собирать и нечего прятать.
Если у вас один фронтенд, одна команда, один сервис за спиной, экран собирается одним запросом, а токенов в браузере нет по другим причинам — вы добавите лишний сервис, лишний деплой и лишний шаг в цепочке. Sam Newman, автор канонического описания паттерна, оговаривает это отдельно: для приложения с одним только веб-интерфейсом такой слой оправдан, лишь если на сервере действительно есть что агрегировать.
И наоборот: как только у экрана появляется своя команда со своим темпом изменений — или как только в браузере оказывается что-то, чего там быть не должно, — слой начинает окупаться.
TL;DR
- BFF — это не слой между фронтендом и бэкендом «вообще». Это отдельный бэкенд на каждый пользовательский опыт, который принадлежит команде этого опыта.
- Придумали его ради двух вещей: убрать круговые задержки и убрать очередь на изменение общего API. О том, где хранится токен, в исходных текстах нет ни слова.
- Вторая жизнь паттерна началась в стандартах: черновик IETF по OAuth в браузере настойчиво рекомендует BFF, потому что при нём токен физически не попадает в JavaScript.
- Чего BFF не делает: он не спасает от XSS. Скрипт, выполняющийся в вашем origin, всё так же прочитает данные пользователя — просто чужими руками. Это записано в самом черновике.
Зачем это знать?
Потому что «BFF» стало словом, которым называют что угодно между браузером и сервисами: прокси, шлюз, «наш api-роут в Next.js». Из-за этого разговор об архитектуре превращается в спор о названиях, а в код попадают решения, взятые из статьи про совсем другую задачу.
У паттерна есть авторы, даты публикаций и две разные причины существования, разнесённые на десятилетие. Первая — производительность и автономия команд: 2012 год у Netflix, 2015-й у SoundCloud. Вторая — хранение токенов, и она появилась не у архитекторов, а в тексте IETF. Понимать, какая из двух причин ваша, важнее, чем знать определение: от этого зависит, сколько BFF заводить, кому они принадлежат и что считать провалом.
Модель в голове
Представьте переводчика на переговорах. Универсальный API — это словарь: он честно переводит каждое слово, но чтобы понять фразу, вам придётся заглянуть в него семь раз, и словарь не знает, что вы вообще-то ведёте переговоры о поставке. Переводчик знает: он слушает всю фразу, сам сверяется с чем нужно и отдаёт вам один готовый ответ — на вашем языке и в вашем контексте.
Ключевое в этой метафоре — чей переводчик. Он нанят вашей стороной и уходит вместе с ней. Это и есть главное свойство BFF: он принадлежит команде фронтенда, а не команде платформы, и меняется вместе с экраном, а не вместе с доменной моделью.
Откуда это на самом деле взялось
История начинается не с BFF, а с Netflix. В июле 2012 года Daniel Jacobson описал, почему их универсальный REST API перестал работать: компания поддерживала, по его словам, более 800 типов устройств, и один и тот же ответ подходил всем одинаково плохо. Вывод в статье сформулирован жёстко — такой API удобен тому, кто его отдаёт, а не тому, кто его потребляет.
Netflix решил это не «ещё одним эндпоинтом», а сменой владельца кода: UI-команды получили право писать серверные адаптеры под свои экраны. Через полгода Ben Christensen в продолжении описал масштаб — 2+ миллиарда входящих запросов в день — и главный выигрыш: собрать экран одним обращением, чтобы WAN-задержка платилась один раз.
Обратите внимание на то, чего в этих двух текстах нет. В них ни разу не обсуждается, где лежит токен. Это статьи про задержку и про то, кто пишет код.
Само название появилось в сентябре 2015 года: Phil Calçado, инженер SoundCloud, описал их путь — монолит, затем публичный API для всех клиентов сразу, затем упор в три стены. Третья стена самая недооценённая: каждый новый эндпоинт приходилось долго обсуждать, чтобы он не оказался слишком узким под конкретный экран. Под новое iOS-приложение команда завела отдельный бэкенд — «разные бэкенды для разных фронтендов».
Через два месяца Sam Newman оформил это в паттерн — и заодно сохранил для нас правило, которое цитируют чаще всего. Сам он приписывает его Stewart Gleadow, а тот, в свою очередь, Phil Calçado и Mustafa Sezgin:
One experience, one BFF.
Дословно: «один пользовательский опыт — один BFF». Не «один на фронтенд», не «один на команду», не «один на приложение». Единица деления — опыт: если iOS и Android почти одинаковы, им хватит одного BFF; если они разошлись — нужны два. Рядом Newman приводит наблюдение Pete Hodgson: на практике BFF лучше всего ложатся на границы команд, и именно структура команд должна определять их количество, — потому что делить надо то, что кто-то потом сопровождает.
Первая причина: круговые задержки складываются
Аргумент 2012 года выглядит очевидным ровно до тех пор, пока его не измеришь. Сеть в разработке кажется бесплатной, потому что на localhost она почти бесплатная.
Ниже — измерение. Реальный браузер против работающего приложения, задержка 150 мс на запрос, наложенная через Chrome DevTools Protocol; каждое число — медиана трёх прогонов. Пунктир и плитки — арифметика по вашим числам, а не измерение; ползунки нужны, чтобы подставить свои.
6 запросов из браузера, последовательно
900 мс
6 × 150 мс. Задержка платится столько раз, сколько сделано обращений.
один запрос к BFF, 6 — внутри
154.3 мс
150 мс + 6 × 0.71 мс. Второе слагаемое — измеренная медиана одного обращения к Go-сервису с той же машины.
Здесь важен не сам факт «шесть запросов дольше одного», а то, во что превращается разница. Шесть последовательных обращений при 150 мс — это 972 мс: почти секунда, за которую на сервере ничего сложного не произошло. Тот же экран за одним обращением к BFF стоит одну задержку плюс работу внутри датацентра, где обращение к соседнему сервису обошлось в 0,71 мс по медиане двадцати замеров.
Отсюда — важная оговорка, о которой в пересказах обычно забывают. Newman высказывает её осторожно — «I suspect», — но по сути прямо: для приложения с одним только веб-интерфейсом BFF оправдан, лишь если на сервере действительно есть что агрегировать. Если ваш экран — это один запрос к одному сервису, вы получите лишний сетевой прыжок и лишний деплой, а взамен ничего.
Вторая причина появилась десятилетием позже
Безопасность в текстах 2015 года упоминается — но совсем другая. У Calçado это первая из трёх проблем универсального API: приходилось всюду проверять OAuth-scope и мешать подделке ключей «официального приложения». Речь о том, кому можно вызывать эндпоинт, а не о том, где лежит токен. Аргумент про хранение токена пришёл позже и из другого места — из стандартизации.
Черновик IETF «OAuth 2.0 for Browser-Based Applications» (ревизия 27, июль 2026; передан в IESG и ждёт публикации как BCP) разбирает три архитектуры в порядке убывания безопасности, ставит BFF первой и настойчиво рекомендует её для бизнес-приложений, чувствительных приложений и всего, что работает с персональными данными. Её обязанности он определяет тремя пунктами: BFF выступает конфиденциальным OAuth-клиентом, хранит access- и refresh-токены внутри cookie-сессии и сам подставляет нужный токен в запросы к ресурсному серверу.
Смысл конструкции черновик формулирует одной фразой: токены доступны только BFF, и в браузере извлекать нечего.
Это проверяемое утверждение, а не декларация. Ниже — измерение того, что достаёт скрипт, выполняющийся на странице, при трёх способах хранить сессию. Колонка «за BFF» — дословный вывод прогона против живого стенда (Next.js в продакшен-режиме перед Go-сервисом перед PostgreSQL); две другие сняты тем же способом на отдельной странице, куда тот же токен положили в localStorage и в куку без HttpOnly.
> localStorage.getItem("access_token")
Токен в localStorage
SPA ходит в API сама
утечка
"tok_1f3a9c8e2b7d4a06"
Тот самый токен, который был туда положен.
Токен в куке без HttpOnly
SPA ходит в API сама
недоступно
null
В localStorage его нет — он в куке.
Сессия в HttpOnly-куке за BFF
браузер знает только BFF
недоступно
null
Измерено: localStorage приложения пуст, ключей нет вообще.
HttpOnly. Обратите внимание на третью пробу: колонка «за BFF» отличается от остальных только тем, что у скрипта нет токена, — данные он получает всё равно.Первые две пробы подтверждают ровно то, что обещано, и заодно объясняют, почему OWASP в Session Management Cheat Sheet прямо запрещает класть токены и идентификаторы сессий в localStorage или sessionStorage: там их видит любой скрипт вашего origin.
А вот третья проба — самое полезное, что есть в этой статье.
Чего BFF не делает
Переключите визуализацию выше на третью пробу. Скрипт, у которого нет токена, спокойно читает закладки пользователя: он просто делает fetch к своему же origin, а куку прикладывает браузер.
Это не дефект реализации. Черновик IETF называет эту атаку прямо — «проксирование запросов через браузер пользователя», раздел 5.1.4: вредоносный код выполняется в том же контексте, что и легитимное приложение, и потому может воспроизводить его поведение. В разборе BFF (раздел 6.1.4.1) этот сценарий остаётся единственным, который паттерн не закрывает, и там же сказано: помешать такому нельзя ничем, кроме недопущения самого выполнения чужого кода.
Практический вывод, который стоит проговорить вслух на любом ревью архитектуры:
BFF меняет последствия XSS, а не вероятность XSS. Украсть долгоживущий токен и уйти с ним в свою инфраструктуру атакующий больше не может. Действовать от имени пользователя, пока открыта вкладка, — по-прежнему может.
Разница реальная и большая: украденный refresh-токен работает после закрытия вкладки, из другой страны и без жертвы. Но продавать BFF как «защиту от XSS» — значит обещать не то, что он делает.
Что стандарт требует от куки
Раз сессия переехала в куку, требования к ней перестают быть делом вкуса. Черновик формулирует их в разделе 6.1.3.2 и 6.1.3.3 нормативной лексикой RFC 2119:
| Требование | Уровень | Что это значит |
|---|---|---|
Secure | MUST | Кука не уходит по открытому HTTP |
HttpOnly | MUST | Куку не видно из document.cookie |
SameSite=Strict | SHOULD | Кука не прикладывается к кросс-сайтовым запросам вообще |
| Защита от CSRF | MUST | Отдельный механизм, не заменяемый предыдущим пунктом |
Последние две строки стоит развести, потому что их постоянно путают. SameSite — это поведение браузера, а не проверка на вашем сервере. rfc6265bis в разделе 5.6.7.1 говорит об этом без обиняков: режим Lax — разумная эшелонированная защита против CSRF, использующих небезопасные методы, но не полноценная защита от CSRF как класса атак. OWASP формулирует то же самое как правило: относиться к SameSite как к слою в глубину и сочетать его с CSRF-токеном, а не полагаться на него одного.
Отсюда — synchronizer token pattern: сервер выдаёт токен, привязанный к сессии, и на каждом изменяющем запросе сверяет пришедшее значение с сохранённым. Кросс-сайтовая форма может заставить браузер приложить куку, но не может прочитать ответ вашего origin — а значит, не узнает токен.
Честное расхождение: почему здесь Lax, а не Strict
Приложение, на котором сняты измерения выше, ставит SameSite=Lax. Черновик говорит SHOULD ... Strict. Это осознанное отклонение, и вот его причина.
Ссылки на подтверждение почты и сброс пароля приходят письмом. Переход по такой ссылке — кросс-сайтовая навигация верхнего уровня: письмо открыто в почтовом клиенте, а не на вашем сайте. При Strict браузер не приложит куку к этому переходу, и человек, только что подтвердивший адрес, попадёт на страницу, которая считает его гостем. При Lax кука прикладывается к навигациям верхнего уровня безопасными методами — то есть ровно к переходу по ссылке из письма, но не к кросс-сайтовому POST.
SHOULD в RFC 2119 и означает эту ситуацию: требование можно не выполнить, если вы поняли последствия. Последствие здесь — ослабленный первый эшелон защиты от CSRF, и он компенсирован вторым: synchronizer token с постоянным по времени сравнением плюс проверка заголовка Origin. Проба «изменяющий запрос без CSRF-заголовка» в измерении выше — это как раз он: запрос из того же origin, с валидной кукой, но без токена получает 403.
Что здесь важно методологически: отклонение от SHOULD перестаёт быть проблемой ровно тогда, когда оно записано вместе с причиной и с тем, чем его закрыли. Незаписанное отклонение — это просто баг, о котором пока никто не знает.
Сколько заводить BFF и чем за них платят
Правило «один опыт — один BFF» звучит просто и ломается на втором BFF. Newman не прячет проблему: между BFF накапливается дублирование. Чего он делать не советует — сливать их обратно в «общий агрегирующий edge-сервис»: такой сервис, по его наблюдению, раз за разом превращается в раздутый код с перемешанными задачами. То есть это тот самый универсальный API, от которого уходили, только с новым названием.
К самому дублированию он относится спокойно: «I am fairly relaxed about duplicated code across services». Это не запрет на вынос общего кода — Newman перечисляет и общую библиотеку, и отдельный сервис как варианты, — но предупреждает, что библиотека является главным источником связности, особенно если через неё генерируются клиенты к нижележащим сервисам. Практический смысл: дублирование между BFF — цена, которую платят осознанно, и выносить общее стоит тогда, когда оно уже устоялось, а не при первом совпадении.
Практическая рамка: BFF оправдан, когда у экрана есть свой темп изменений и своя команда. Если у вас один фронтенд, одна команда и один сервис за спиной — вы платите за прыжок и деплой, не получая ни автономии, ни агрегации. В этом случае честнее сказать «у нас серверный слой для сессий», чем называть его BFF: слово тянет за собой ожидания, которых конструкция не оправдывает.
Чем BFF не является
Это не API Gateway. Шлюз — инфраструктурный компонент, общий для всех потребителей: маршрутизация, лимиты, терминация TLS, аутентификация на входе. BFF — прикладной код, принадлежащий одной команде и знающий про её экраны. Они не конкурируют и часто стоят вместе: шлюз на входе, BFF за ним.
Это не «просто прокси». Прокси, который один в один пересылает запросы, не даёт ни агрегации, ни автономии — двух вещей, ради которых паттерн придумали. Он может быть полезен ради токенов, но тогда это следует называть своим именем, а не паттерном 2015 года.
Это не GraphQL. GraphQL решает соседнюю задачу — «клиент сам описывает, что ему нужно», — и решает её схемой, а не выделенным бэкендом на каждый опыт. Заметьте, что Netflix в 2012 году выбрал третий вариант: не универсальный API и не язык запросов, а серверные адаптеры, которые пишут UI-команды. Все три подхода отвечают на один вопрос, но по-разному распределяют, кто пишет код и кто за него отвечает.
Как это выглядит в этом приложении
Читатель этого сайта не ходит в Go-сервис напрямую: браузер знает только маршруты Next.js под /api, а токен сессии живёт в HttpOnly-куке, к которой у скриптов нет доступа. Модуль, из которого делаются исходящие вызовы, помечен server-only — импорт его из клиентского компонента ломает сборку, а не проходит ревью.
Это BFF ровно во втором смысле — том, который пришёл из черновика IETF. Агрегации здесь почти нет: маршруты в основном один к одному соответствуют вызовам Go-сервиса. По критерию Newman это как раз тот случай, когда BFF ради агрегации был бы не нужен, — и он здесь стоит не ради неё.
Что здесь стандарт, а что — решение команды
| Утверждение | Статус |
|---|---|
Токен не должен лежать в localStorage | Требование OWASP |
Кука сессии — Secure и HttpOnly | MUST черновика IETF |
SameSite=Strict | SHOULD черновика IETF |
| Отдельная защита от CSRF обязательна | MUST черновика IETF |
SameSite в одиночку от CSRF не защищает | Прямо сказано в rfc6265bis |
| «Один опыт — один BFF» | Рекомендация Newman, не стандарт |
| BFF нужен веб-приложению только при заметной агрегации | Оговорка Newman, не стандарт |
| Дублирование между BFF терпимо | Позиция Newman, спорная |
Lax вместо Strict в этом приложении | Решение команды, обосновано выше |
Разделение здесь не формальность. Первые пять строк — то, за нарушение чего вам справедливо укажут на спецификацию. Следующие три — опыт конкретных людей, полученный на конкретных системах, и их можно оспаривать. Последняя — ваша собственная ответственность, и единственное, что делает её приемлемой, — записанная причина.
Что стоит помнить
Паттерн, у которого две несвязанные причины существования, опаснее обычного: легко внедрить его ради одной и решить, что получил обе. BFF, поставленный ради токенов, не ускорит экран сам по себе. BFF, поставленный ради агрегации, не защитит токен, если вы всё равно отдаёте его в JavaScript.
И ни один из них не остановит XSS — он лишь решает, что именно атакующий унесёт с собой, когда вкладка закроется.
Частые заблуждения
«BFF защищает от XSS».
Он меняет последствия XSS, а не вероятность. Токен действительно не достать из браузера, но скрипт выполняется внутри вашего origin и может слать запросы к BFF от имени пользователя — куку приложит браузер. Черновик IETF называет это проксированием запросов через браузер пользователя (раздел 5.1.4), а в разборе BFF (6.1.4.1) оставляет её единственным работающим сценарием и говорит, что помешать этому нельзя ничем, кроме недопущения выполнения чужого кода.
«BFF придумали ради безопасности».
О хранении токенов в первоисточниках 2012–2015 годов (Netflix, Calçado, Newman) нет ни слова: там речь о круговых задержках и о том, кто имеет право менять код эндпоинта. Безопасность у Calçado упоминается, но другая — проверки OAuth-scope и подделка ключей «официального приложения», то есть кому можно вызывать эндпоинт. Аргумент про то, где лежит токен, появился десятилетием позже — в черновике IETF по OAuth в браузере.
«Поставили SameSite — CSRF закрыт».
rfc6265bis в разделе 5.6.7.1 говорит, что Lax — разумная эшелонированная защита против CSRF на небезопасных методах, но не полноценная защита от CSRF как класса. OWASP формулирует то же самое как правило: сочетать SameSite с CSRF-токеном, а не полагаться на него одного. Черновик IETF выносит это в отдельное MUST.
«BFF — это то же самое, что API Gateway, только модное».
Шлюз общий для всех потребителей и живёт в инфраструктуре: маршрутизация, лимиты, TLS. BFF принадлежит одной продуктовой команде и знает про её экраны. Они не взаимозаменяемы и часто стоят вместе — шлюз на входе, BFF за ним.
«Раз паттерн хороший, BFF нужен любому веб-приложению».
Newman оговаривает это отдельно (осторожным «I suspect»): для приложения с одним только веб-интерфейсом BFF оправдан, лишь если на сервере действительно есть заметная агрегация. Без неё вы получаете лишний сетевой прыжок и лишний деплой. Отдельная история — если слой нужен ради токенов: тогда это верное решение, но по другой причине, и называть его стоит честно.
Проверка знаний
В приложении сессия лежит в HttpOnly-куке за BFF. На странице сработала XSS. Что атакующий может сделать?
Источники и что читать дальше
8 ИСТОЧНИКОВ
- Phil Calçado — The Back-end for Front-end Pattern (BFF)Источник. 18 сентября 2015. Инженер SoundCloud описывает, как их монолит превратился в one-size-fits-all API и почему под новое iOS-приложение завели отдельный бэкенд. Текст, из которого пошло само название.https://philcalcado.com/2015/09/18/the_back_end_for_front_end_pattern_bff.html
- Sam Newman — Backends For FrontendsИсточник. Каноническое описание паттерна (первая публикация — 18 ноября 2015). Здесь собраны правило «one experience, one BFF» (Newman приводит его от Stewart Gleadow), наблюдение Pete Hodgson про границы команд и предупреждение о том, чем кончаются попытки слить BFF обратно в общий edge-сервис.https://samnewman.io/patterns/architectural/bff/
- Daniel Jacobson — Embracing the Differences: Inside the Netflix API RedesignИсточник. Netflix TechBlog, 9 июля 2012. Отказ от универсального REST API в пользу адаптеров, которые пишут сами UI-команды. Здесь же цифра «более 800 типов устройств».https://netflixtechblog.com/embracing-the-differences-inside-the-netflix-api-redesign-15fd8b3dc49d
- Ben Christensen — Optimizing the Netflix APIИсточник. Netflix TechBlog, 15 января 2013. Продолжение той же истории: «2+ миллиарда входящих запросов в день» и объяснение, почему WAN-задержку выгодно платить один раз.https://netflixtechblog.com/optimizing-the-netflix-api-5c9ac715cf19
- draft-ietf-oauth-browser-based-apps — OAuth 2.0 for Browser-Based ApplicationsОфициальная документация. Черновик IETF, ревизия 27 (6 июля 2026), передан в IESG и ждёт публикации как BCP. Раздел 6.1 описывает BFF как архитектуру и формулирует требования к кукам и CSRF; 5.1.4 разбирает атаку, которую паттерн не закрывает, а 6.1.4.1 честно оставляет её единственным работающим сценарием.https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps-27
- draft-ietf-httpbis-rfc6265bis — Cookies: HTTP State Management MechanismОфициальная документация. Рабочая версия замены RFC 6265. Раздел 5.6.7.1 определяет режимы SameSite и прямо говорит, что Lax — не полноценная защита от CSRF.https://httpwg.org/http-extensions/draft-ietf-httpbis-rfc6265bis.html
- OWASP — Cross-Site Request Forgery Prevention Cheat SheetОфициальная документация. Synchronizer Token Pattern, проверка Origin/Referer и раздел о том, когда SameSite достаточно самой по себе (спойлер: почти никогда).https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
- OWASP — Session Management Cheat SheetОфициальная документация. Требования к идентификатору сессии: минимум 64 бита энтропии, флаги Secure и HttpOnly, прямой запрет хранить токены в localStorage и sessionStorage.https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html