Skip to content

Router: preflight берёт внешний CorsMiddleware, основной запрос — внутренний #718

Closed

Description

@biz87

Исправлено 14.09. В первой версии предлагалось брать для preflight последний CorsMiddleware стека. Это неверно: при таком решении ломается preflight для origin, разрешённого только внешней группой. Разбор — в ревью #727. Также неверным было утверждение, что до #708 расхождения не было. Разделы ниже обновлены.

Проблема

После #708 Router::dispatchOptionsPreflight() отвечает на OPTIONS через resolveFirstCorsMiddleware() — первый CorsMiddleware в стеке маршрута. Стек собирается как «внешние группы, затем внутренние, затем middleware маршрута»: в group() — array_merge($previousMiddlewares, $middlewares), в addRoute() — array_merge($this->currentMiddlewares, $middlewares). Первым оказывается самый внешний.

Для обычного запроса отрабатывают все middleware по порядку. CorsMiddleware::applyOriginHeadersIfAllowed() ставит заголовки, только если origin разрешён этому экземпляру, и ничего не снимает. Поэтому заголовки основного запроса — это накопленный результат всех CorsMiddleware стека, а не одного из них.

Preflight вызывает один экземпляр, и если у вложенной группы или маршрута свой CorsMiddleware с другими allowed_origins, preflight и основной запрос отвечают по-разному. Пример: внешняя группа /api/v1 пускает https://outer.example, вложенная /nested — https://inner.example.

Origin GET Preflight: первый экземпляр (beta) Preflight: последний экземпляр
только outer заголовки outer заголовки outer без заголовков
только inner заголовки inner без заголовков заголовки inner
никому без заголовков без заголовков без заголовков

Выбор любого одного экземпляра оставляет расхождение, только на разных origin.

Отказ в сторону запрета — дыры нет. В текущих маршрутах CorsMiddleware один, на /api/v1 в config/routes/web.php, так что проявится это у дополнений с вложенными группами.

Расхождение было и до #708: внешний CorsMiddleware на OPTIONS возвращал ответ и останавливал стек, внутренний не вызывался.

Что сделать

На preflight вызывать все CorsMiddleware маршрута в порядке стека и отдавать ответ последнего. TokenMiddleware, RateLimitMiddleware и handler по-прежнему не вызывать (#706).

Критерии приёмки

  • Preflight вызывает все CorsMiddleware стека маршрута в порядке стека, остальные middleware и handler — нет
  • Тест сравнивает фактические CORS-заголовки preflight и основного запроса для origin «только outer», «только inner», «оба уровня», «никому»; у уровней разные allow_credentials и max_age

Контекст

Activity

  1. added
    priority: lowНизкий приоритет, когда будет время
    bugSomething isn't working
    on Sep 13, 2026
  2. self-assigned this
    on Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpriority: lowНизкий приоритет, когда будет время

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions