CORS

За последнее десятилетие применение технологии одностраничных приложений стало обычным явлением при создании веб-приложений. Сегодня во фронтенд-разработке господствуют такие фреймворки, как Angular и Vue, и такие библиотеки, как React, обеспечивающие базовую платформу для этих приложений. Преимущества заключаются в том, что они обслуживают API фронтенда и бэкенда из одного домена. Но есть случаи, когда фронтенд (например, web.myapp.com) и бэкенд (например, api.myapp.com) обслуживаются из отдельных поддоменов. Также иногда мы разрешаем доступ из разных источников через API бэкенда только для среды разработки.

Обмен ресурсами между разными источниками (Cross-origin resource sharing — CORS) — это механизм, реализуемый в веб-браузерах для разрешения или отклонения запросов, поступающих из другого домена в веб-приложение. С помощью CORS веб-браузеры и веб-серверы согласовывают стандартный протокол, определяющий, стоит ли разрешить доступ к определенным ресурсам. Помните, что принудительное применение CORS со стороны бэкенда не означает, что боты или любой другой механизм не могут получить доступ к ресурсам сервера.

Нужно ли использовать CORS?

Нужно ли разрешать CORS для веб-приложений? Стоит сказать, что в большинстве случаев вам не нужно беспокоиться о CORS, поскольку веб-приложение обслуживается из одного домена. Однако в приложении могут быть специальные функции, такие как возможность встраивания страницы (например, Form, Video) за пределами основного домена веб-приложения. В таком случае можно рассмотреть возможность включения CORS в бэкенде только для этой конкретной функции.

Недостатки CORS

Наиболее очевидным недостатком CORS, помимо безопасности, является влияние на производительность в веб-приложениях. Когда фронтенд отправляет HTTP-запрос на другой домен или поддомен, браузер отправляет дополнительный HTTP-запрос (предзапрос), чтобы узнать, принимает ли сервер сообщения из домена отправителя.

Таким образом, для каждого HTTP-запроса, запускаемого фронтендом, браузер должен отправить два HTTP-запроса, что увеличивает общее время ответа. В большинстве случаев добавленная задержка заметна в веб-приложениях и отрицательно влияет на опыт для пользователей.

CORS в одностраничных приложениях

Когда дело доходит до одностраничных приложений, использование CORS становится гораздо более заметным. Веб-браузеры не будут рассматривать предзапрос, если веб-приложение использует только HTTP-заголовки (Accept, Accept-Language, DPR, Downlink, Save-Data, Viewport-Width, Width, Content-Language, Content-Type, за исключением значений application/x-www-form-urlencoded, multipart/form-data, text/plan) и HTTP-методы (GET, HEAD, POST) для вызовов API бэкенда. Скорее всего, вам понадобятся эти HTTP-заголовки и HTTP-методы в одностраничных приложениях.

В этих приложениях URL API бэкенда определен во фронтенде как переменная для операций сервера. Кроме того, мы даже можем предоставить CORS в API бэкенда, поскольку сервер разработки для API фронтенда и бэкенда, возможно, работает на двух разных портах. Среда разработки также может повлиять на настройки в окружении продакшна, где вы, возможно, развернете API фронтенда и бэкенда на разных поддоменах.

Стоит ли идти в этом направлении? Давайте рассмотрим способы, как избежать использования CORS как для среды разработки, так и для окружения продакшна.

Как избежать CORS в среде разработки

Сегодня большинство серверов разработки для фронтенда используют NodeJS. Большинство из этих Node-серверов поддерживают настройку прокси. Кроме того, Angular, React и Vue поставляются с Webpack, который имеет встроенную поддержку настройки прокси.

Что именно делает эта настройка прокси?

Предположим, что ваше веб-приложение запущено по адресу http://localhost:4200, а API бэкенда — http://localhost:3000/api/<resource>. Фронтенд должен хранить URL-адрес и порт API бэкенда, чтобы запускать приложение локально. Кроме того, вам также нужно будет включить CORS в API бэкенда, разрешив вызовы API, поступающие из фронтенда. В данном случае API фронтенда и бэкенда одинаковы (http://localhost), но порты разные, и браузер считает их разными источниками.

Чтобы избежать всех вышеперечисленных хлопот, можно использовать настройку прокси на серверах фронтенд-разработки. Когда вы используете прокси, вам нужно хранить только относительный путь (/api) во фронтенд-приложении. При локальном запуске приложения фронтенд попытается получить доступ к API бэкэнда, используя тот же домен и порт (http://localhost:4200/api/<resource>), а браузер не будет беспокоиться о CORS.

На этом этапе прокси делает свое дело. Внутри настройки прокси можно указать перенаправление любых запросов, поступающих для пути /api, на http://localhost:3000 на сервер фронтенд-разработки.

Поскольку сервер разработки является посредником, взаимодействующим с API бэкенда, он может безопасно избежать CORS. В приведенном ниже примере показано, как добавить настройку прокси на сервере разработки Webpack.

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://localhost:3000'
    }
  }
};

В качестве альтернативного подхода вы можете запустить веб-браузер со специальными флагами, чтобы отключить CORS для локального тестирования, если вы не хотите использовать относительные пути во фронтенде для API бэкенда. Например, запустить браузер Chrome без CORS.

Как избежать CORS в окружении продакшна

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

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

Кроме того, важно укрепить API бэкенда для CORS, разрешив доступ только из одного источника.

Заключение

Я надеюсь, что вы разобрались в том, как CORS влияет на производительность и в чем преимущества избежания его использования в одностраничных приложениях. Безопасность является основной причиной создания CORS в современных браузерах, поэтому очень важно знать основы его работы с этой точки зрения. Здесь мы рассмотрели лишь способы избежания CORS в одностраничных приложениях.

Поскольку мы затронули тему использования прокси, чтобы избежать CORS, вы, возможно, задаетесь вопросом, насколько сложно настроить прокси, когда API фронтенда и бэкенда работают в отдельных сервисах. Тем не менее выполнить настройку проще, чем вы думаете. Например, при настройке прокси сервера разработки для Angular, React или Vue необходимо добавить несколько строк в конфигурационном файле Webpack, чтобы перенаправить запросы к API бэкенда для избежания CORS. То же самое относится и к окружениям продакшна, поскольку существуют устоявшиеся способы реализации маршрутизации на основе пути URL.

Однако вы должны установить правильное преобразование пути для API бэкенда, чтобы избежать необходимости обновлять настройку прокси при каждом добавлении новой конечной точки. Например, если вы используете базовый путь, такой как \api\, проще написать простое правило для маршрутизации запросов к API бэкенда для всех запросов, имеющих базовый путь, и fallback к фронтенд-ресурсам для других путей HTTP.

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

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Ashan Fernando: How and Why You Should Avoid CORS in Single Page Apps

Предыдущая статьяПочему мы создали платформу для инженерии машинного обучения, а не науки о данных
Следующая статьяИдиоматический Python для новичков