KtorMiddleCoding

Как реализовать CORS в приложении Ktor?

Устанавливайте плагин CORS через install(CORS) { allowHost(...); allowMethod(...); allowHeader(...) }. При allowCredentials = true нельзя использовать anyHost() — браузер требует точный Origin в ответе.

CORS в Ktor: плагин и его конфигурация

Ktor реализует CORS через встроенный плагин CORS из артефакта ktor-server-cors. Плагин перехватывает preflight-запросы (OPTIONS) и добавляет нужные заголовки к реальным ответам, не требуя ручной обработки в каждом маршруте.

Подключение зависимости

// build.gradle.kts
dependencies {
    implementation("io.ktor:ktor-server-cors:2.3.12")
}

Базовая конфигурация

import io.ktor.server.application.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.http.*

fun Application.configureHTTP() {
    install(CORS) {
        // Разрешённые источники
        allowHost("example.com", schemes = listOf("https"))
        allowHost("localhost:3000", schemes = listOf("http"))

        // Разрешённые HTTP-методы
        allowMethod(HttpMethod.Get)
        allowMethod(HttpMethod.Post)
        allowMethod(HttpMethod.Put)
        allowMethod(HttpMethod.Delete)
        allowMethod(HttpMethod.Patch)

        // Разрешённые заголовки запроса
        allowHeader(HttpHeaders.ContentType)
        allowHeader(HttpHeaders.Authorization)
        allowHeader("X-Custom-Header")

        // Разрешить передачу куки и заголовков авторизации
        allowCredentials = true

        // Заголовки, которые браузер может читать из ответа
        exposeHeader(HttpHeaders.ContentDisposition)

        // Время жизни preflight-кэша в браузере (сек)
        maxAgeInSeconds = 3600L
    }
}

Разрешить все источники (только для разработки)

install(CORS) {
    anyHost()  // НЕ использовать в продакшне
    allowMethod(HttpMethod.Get)
    allowMethod(HttpMethod.Post)
    allowHeader(HttpHeaders.ContentType)
}

Паттерн wildcard-поддомена

Ktor не поддерживает wildcards вида *.example.com нативно. Для динамической проверки источника используйте allowOrigins { origin -> ... }:

install(CORS) {
    allowOrigins { origin ->
        origin.endsWith(".example.com") || origin == "https://example.com"
    }
    allowMethod(HttpMethod.Get)
    allowHeader(HttpHeaders.ContentType)
    allowCredentials = true
}

Конфигурация через application.conf (HOCON)

ktor {
  application {
    modules = [ com.example.ApplicationKt.module ]
  }
}

cors {
  allowedHosts = ["example.com", "localhost:3000"]
}

Затем в коде читайте через environment.config.property("cors.allowedHosts").getList() и динамически вызывайте allowHost().

Важные нюансы работы плагина

  • Плагин устанавливается глобально и применяется ко всем маршрутам. Для route-level CORS нужно оборачивать роутинг вручную.
  • Preflight (OPTIONS) перехватывается плагином и не достигает ваших обработчиков.
  • При allowCredentials = true браузер требует точного указания Access-Control-Allow-Origin; anyHost() в этом режиме недопустим.

Подводные камни

  • anyHost() совместно с allowCredentials = true запрещён спецификацией CORS и вызовет исключение при старте в Ktor 2.x.
  • Если не добавить allowHeader(HttpHeaders.ContentType), запросы с JSON-телом (тип application/json) будут блокироваться как нераспознанные preflight.
  • Заголовки ответа, не перечисленные в exposeHeader(), недоступны JavaScript-клиенту даже при правильном CORS.
  • CORS-плагин необходимо устанавливать до плагина Authentication, иначе preflight может получить 401 вместо 204.
  • Ktor проверяет заголовок Origin строго: http://localhost:3000 и http://localhost:3000/ с трейлинг-слешем — разные строки.
  • В Ktor 2.x метод называется allowOrigins { }, а не host() из Ktor 1.x — при миграции старые проекты ломаются с Unresolved reference.
  • При maxAgeInSeconds браузер кэширует preflight-ответ; изменение CORS-политики на сервере не сразу отражается у клиентов без hard refresh.
  • Wildcard * в Access-Control-Allow-Headers не работает в IE и некоторых мобильных браузерах — явно перечисляйте каждый заголовок.

Common mistakes

  • Путать термин «ktor cors» с соседним механизмом Ktor.
  • Не называть границу lifecycle, transaction, thread или request для «ktor cors».
  • Игнорировать production-эффекты «ktor cors»: latency, SQL shape, memory, security или observability.

What the interviewer is testing

  • Попросить объяснить механизм «ktor cors» на минимальном примере.
  • Проверить, видит ли кандидат failure mode и диагностику для «ktor cors».
  • Уточнить, какие настройки или API меняют «ktor cors» в реальном сервисе.

Sources

Related topics

Как реализовать CORS в приложении Ktor? | Talanto