Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

CORS для фронтенда

Часто фронтенд работает на одном адресе, например http://localhost:5173 (сервер разработки Vite), а API — на другом, http://localhost:8080. Браузер по умолчанию не даёт JavaScript со страницы одного источника (origin: схема, домен и порт) читать ответы другого. Механизм, который позволяет серверу разрешить такие запросы, называется CORS (Cross-Origin Resource Sharing). Без него код fetch("http://localhost:8080/posts") на странице фронтенда упадёт с ошибкой в консоли браузера.

Как работает CORS

  1. Браузер добавляет к запросу заголовок Origin с адресом страницы.
  2. Для «непростых» запросов, например POST с JSON или с заголовком Authorization, браузер сначала отправляет предварительный запрос (preflight) методом OPTIONS.
  3. Сервер отвечает заголовком Access-Control-Allow-Origin. Если источника там нет, браузер не отдаст ответ странице.

curl и другие программы правила CORS не проверяют — это защита именно браузера.

Плагин CORS

В Javalin есть встроенный плагин. Включите его в настройках:

// src/main/java/com/example/blog/Main.java
config.bundledPlugins.enableCors(cors -> {
    cors.addRule(rule -> rule.allowHost("http://localhost:5173"));
});

addRule добавляет правило, allowHost перечисляет разрешённые источники. Плагин сам отвечает на запросы OPTIONS и добавляет нужные заголовки.

Проверка

Изобразим браузер: передадим заголовок Origin.

curl -i -H "Origin: http://localhost:5173" http://localhost:8080/posts
HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: http://localhost:5173
Vary: Origin

[{"id":1,"title":"Привет, H2","body":"Посты теперь хранятся в базе.","author":"Олег"}]

Теперь предварительный запрос, который браузер отправит перед POST с паролем:

curl -i -X OPTIONS http://localhost:8080/posts \
     -H "Origin: http://localhost:5173" \
     -H "Access-Control-Request-Method: POST" \
     -H "Access-Control-Request-Headers: content-type, authorization"
HTTP/1.1 200 OK
Content-Type: text/plain
Access-Control-Allow-Headers: content-type, authorization
Access-Control-Allow-Methods: POST
Access-Control-Allow-Origin: http://localhost:5173
Vary: Origin

Сервер разрешил метод и заголовки, и браузер отправит настоящий запрос.

Другие настройки правила

НастройкаЧто делает
rule.allowHost("https://blog.example.com")Разрешить конкретные источники
rule.allowHost("*.example.com")Разрешить все поддомены
rule.anyHost()Разрешить всех (заголовок *)
rule.allowCredentials = trueРазрешить куки и заголовок Authorization в запросах браузера
rule.exposeHeader("X-Total")Показать заголовок ответа скрипту
rule.path = "posts*"Применять правило только к части адресов

Частая ошибка

Если источник не входит в список, плагин отвечает кодом 400, хотя тело ответа на месте:

curl -i -H "Origin: http://evil.example" http://localhost:8080/posts
HTTP/1.1 400 Bad Request
Content-Type: application/json

[{"id":1,"title":"Привет, H2","body":"Посты теперь хранятся в базе.","author":"Олег"}]

Заголовка Access-Control-Allow-Origin нет, поэтому браузер не отдаст ответ странице. Увидев в консоли браузера ошибку CORS, сначала сравните адрес страницы с allowHost вплоть до порта и схемы: http://localhost:5173 и http://127.0.0.1:5173 — разные источники.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.