hey guys, i am having some issue with the CORS plu...
# ktor
h
hey guys, i am having some issue with the CORS plugin, this is my code check 🧵 when i curl the url i do not see the allow methods i’ve added
Copy code
curl -X OPTIONS <https://api.local.test/api/v1/posts/groups> -H "Origin: <https://local.test>" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: authorization,content-type" -k -v 2>&1 
| grep -E "HTTP/|access-control|< "
* using HTTP/2
* [HTTP/2] [1] OPENED stream for <https://api.local.test/api/v1/posts/groups>
* [HTTP/2] [1] [:method: OPTIONS]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: api.local.test]
* [HTTP/2] [1] [:path: /api/v1/posts/groups]
* [HTTP/2] [1] [user-agent: curl/8.7.1]
* [HTTP/2] [1] [accept: */*]
* [HTTP/2] [1] [origin: <https://local.test>]
* [HTTP/2] [1] [access-control-request-method: POST]
* [HTTP/2] [1] [access-control-request-headers: authorization,content-type]
> OPTIONS /api/v1/posts/groups HTTP/2
< HTTP/2 200 
< access-control-allow-credentials: true
< access-control-allow-headers: Accept-Encoding, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Authorization, Content-Disposition, Content-Encoding, Content-Type, Origin, Referer, User-Agent, X-Forwarded-Host, X-Http-Method-Override, X-Request-Hash, X-Request-ID, X-Request-Nonce, X-Request-Signature, X-Request-Timestamp, X-Requested-With, X-Total-Count
< access-control-allow-methods: DELETE, OPTIONS, PATCH, PUT
< access-control-allow-origin: <https://local.test>
< access-control-max-age: 3600
< alt-svc: h3=":443"; ma=2592000
< date: Sun, 09 Nov 2025 12:37:25 GMT
< permissions-policy: camera=(), microphone=(), geolocation=()
< referrer-policy: strict-origin-when-cross-origin
< referrer-policy: strict-origin-when-cross-origin
< server: Ktor/3.3.2
< vary: Origin
< via: 1.1 Caddy
< x-content-type-options: nosniff
< x-content-type-options: nosniff
< x-frame-options: DENY
< x-frame-options: DENY
< x-request-id: =66+i+7f=zydipj1qfmru=gd8kgn7a5+svs2h-/ljegk2zxa349y2exu-2k5f/gf
< x-request-id: =66+i+7f=zydipj1qfmru=gd8kgn7a5+svs2h-/ljegk2zxa349y2exu-2k5f/gf
< x-xss-protection: 1; mode=block
< content-length: 0
<
curl
Copy code
fun Application.corsPlugin(
    isDebug: Boolean = BackendComponent.isDebug
) {
    install(CORS) {
        allowNonSimpleContentTypes = true
        allowSameOrigin = true
        allowCredentials = true
        maxAgeInSeconds = 3600L
        allowXHttpMethodOverride()
        allowHeader(HttpHeaders.AccessControlAllowOrigin)
        allowHeader(HttpHeaders.AccessControlAllowMethods)
        allowHeader(HttpHeaders.AccessControlAllowCredentials)
        allowHeader(HttpHeaders.AccessControlAllowHeaders)
        allowHeader(HttpHeaders.AccessControlRequestHeaders)
        allowHeader(HttpHeaders.AccessControlRequestMethod)
        allowHeader(HttpHeaders.AccessControlMaxAge)
        allowHeader(HttpHeaders.Origin)
        allowHeader(HttpHeaders.Referrer)
        allowHeader(HttpHeaders.ContentType)
        allowHeader(HttpHeaders.Authorization)
        allowHeader(HttpHeaders.UserAgent)
        allowHeader(HttpHeaders.Accept)
        allowHeader(HttpHeaders.ContentDisposition)
        allowHeader(HttpHeaders.AcceptEncoding)
        allowHeader(HttpHeaders.AcceptLanguage)
        allowHeader(HttpHeaders.ContentEncoding)
        allowHeader(HttpHeaders.XRequestId)
        allowHeader(HttpHeaders.XTotalCount)
        allowHeader(HttpHeaders.XForwardedHost)
        allowHeader("X-Request-Timestamp")
        allowHeader("X-Request-Signature")
        allowHeader("X-Request-Nonce")
        allowHeader("X-Request-Hash")
        allowHeader("X-Requested-With")

        if (isDebug) {
            allowHost("local.test", schemes = listOf("https", "http"))
            allowHost("api.local.test", schemes = listOf("https", "http"))
            allowHost("localhost:5173", schemes = listOf("https", "http"))
            allowHost("localhost:8080", schemes = listOf("https", "http"))
            allowHost("127.0.0.1:5173", schemes = listOf("https", "http"))
            allowHost("127.0.0.0:5173", schemes = listOf("https", "http"))
        } else {
            allowHost(
                host = BuildKonfig.serverUrl.substringAfter("://").substringBefore("/"),
                schemes = listOf("https")
            )
        }
        
        allowMethod(HttpMethod.Get)
        allowMethod(HttpMethod.Post)
        allowMethod(HttpMethod.Put)
        allowMethod(HttpMethod.Delete)
        allowMethod(HttpMethod.Patch)
        allowMethod(HttpMethod.Head)
        allowMethod(HttpMethod.Options)
    }
}
Copy code
< access-control-allow-methods: DELETE, OPTIONS, PATCH, PUT
as you can see the methods aren’t here, only few of them are but it’s missing GET and POST
so some of my preflight POST methods are failing (the ones that are wrapped in authenticate) from the browser, the ones that are POST and send a body json, am i doing something wrong?
this is the error on the browser
a
Can you please share the TRACE-level logs emitted by the CORS plugin?
h
how do i enable it in the logback?
Copy code
<logger name="io.ktor.server.plugins.cors.routing.CORS" level="TRACE"/>
like this or?
a
Yes, except that the logger name is
io.ktor.server.plugins.cors.CORS
.
h
Copy code
10-11-2025 11:17:04.767 [eventLoopGroupProxy-4-2] 54+d1dmc+z2gf5sqga567d=gtumjeq=u7d6-6knsnzh1stn6jq27rqlacl56u5bx TRACE io.ktor.server.plugins.cors.CORS - Return Forbidden for /api/v1/posts/groups: request has not allowed headers.

10-11-2025 11:17:06.798 [eventLoopGroupProxy-4-2] 1vzy-1lm9uefqyju=x1x689/i6m16/c8m9qje=w8uxwpma80--sun9zks45523nh TRACE io.ktor.server.plugins.cors.CORS - Respond preflight on OPTIONS for /api/v1/posts/groups

10-11-2025 11:17:06.798 [eventLoopGroupProxy-4-2] 1vzy-1lm9uefqyju=x1x689/i6m16/c8m9qje=w8uxwpma80--sun9zks45523nh TRACE io.ktor.server.plugins.cors.CORS - Return Forbidden for /api/v1/posts/groups: request has not allowed headers.

10-11-2025 11:17:10.833 [eventLoopGroupProxy-4-2] -3x3t/bx1-7hp0wqqa8jxw35clw1z90bm-gel27bk=+m9bjb9y=d6tum+=18gzxc TRACE io.ktor.server.plugins.cors.CORS - Respond preflight on OPTIONS for /api/v1/posts/groups

10-11-2025 11:17:10.833 [eventLoopGroupProxy-4-2] -3x3t/bx1-7hp0wqqa8jxw35clw1z90bm-gel27bk=+m9bjb9y=d6tum+=18gzxc TRACE io.ktor.server.plugins.cors.CORS - Return Forbidden for /api/v1/posts/groups: request has not allowed headers.
the original post request
a
Unfortunately, it doesn't log which request headers weren't allowed. Can you put a breakpoint in the
internal fun corsCheckRequestHeaders
method to see which request methods aren't expected?
h
CONTENT_TYPE Content-Type CONTENT_TYPE_JSON application/json CONTENT_TYPE_MULTIPART multipart/form-data weird
👍 1
okay, now i found the issue, i guess it might be a good feature request when we do have TRACES to get this information because i didn’t even know that
corsCheckRequestHeaders
was actually the function i had to utilize to debug, thank you so much
a
I agree that logging the unmatched headers can improve the plugin's transparency. I found the method by searching for the log message.
fist bump 2