-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
In Ktor 3.3.0, the CORS plugin does not correctly include the default HTTP methods (GET, POST, HEAD) in the Access-Control-Allow-Methods response header.
Current implementation in CORSConfig builds the methods header like this:
val methodsListHeaderValue = methods.filterNot { it in CorsDefaultMethods } .map { it.value } .sorted() .joinToString(", ")
Since CorsDefaultMethods = { GET, POST, HEAD }, these methods are filtered out and not included in the final header.
As a result, browsers see an incomplete Access-Control-Allow-Methods header (often containing only OPTIONS), which breaks CORS preflight validation.
To Reproduce
Steps to reproduce the behavior:
Enable the CORS plugin in Ktor 3.3.0 and allow POST.
Send an OPTIONS preflight request from the browser.
Inspect the response headers.
Example output:
Access-Control-Allow-Methods: OPTIONS
Expected output:
Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS
Expected behavior
The Access-Control-Allow-Methods header should always include all configured methods, including the defaults (GET, POST, HEAD).
Proposed Fix
Replace the filtering logic with a distinct collection of methods:
val methodsListHeaderValue = methods.distinct() .map { it.value } .sorted() .joinToString(", ")
This ensures that GET, POST, HEAD are included in the header along with any additional methods.
Environment
Ktor version: 3.3.0
Module: ktor-server-cors
JVM: 17
OS: (e.g. Ubuntu 22.04 / Windows 11)
Additional context
This bug prevents correct CORS preflight handling in browsers, leading to blocked requests even when methods are allowed.