Ktor logo


Building Asynchronous Servers and Clients in Kotlin


Ktor 2.3.0 Released

Read this post in other languages:

We are pleased to announce the release of Ktor 2.3.0. This update brings numerous new features and improvements to the Ktor ecosystem. The following sections provide a brief overview of the major changes in this release:

Core Improvements

Explicit type for WebSockets ContentNegotiation Plugin

The WebSocket ContentNegotiation plugin now supports specifying explicit type:

client.webSocket("ws://localhost:8080/echo") {
    val originalData: Any = Data("hello")
    sendSerialized(originalData, typeInfo<Data>())
    val received = receiveDeserialized<Data>(typeInfo<Data>())

    assertEquals(originalData, received)

It can be helpful if reified parameters are not available.

Structured Concurrency Support for Sockets

The Socket implementation from ktor-network is a CoroutineScope now. It means it supports the cancel() operation to terminate the connection immediately.

The close() continues working as a graceful shutdown and will wait for all pending operations to complete.

Dropping the JS Legacy Configuration

The upcoming Kotlin 1.9.0 release will remove the legacy JS compiler. Starting from Ktor 2.3.0, the legacy compiler is no longer supported. Please consider migrating to the new IR compiler.


Regular Expression Support in Routing

Ktor now allows the use of regular expressions when defining routes. With regex routing, you can match a complex route and capture route parameters with ease:

routing {
    get(Regex("^(?<name>.+)$")) {
        val name = call.parameters["name"]
        call.respondText("Hello, $name!")

Will work as:

$ curl
Hello, Leonid!

You can learn more from the documentation.

Static Content API Cleanup

The Static Content API has been refined and streamlined for a more user-friendly experience. This time we focused on the most common cases and made a simple API for them:

routing {
    staticFiles("/static", File("files"))

Will serve all files from the files directory under the /static path.

And we can also serve files from the resources:

routing {
    staticResources("/static", "assets")

It also configures serving index.html as a default file and supports features like precompression (even for resources), caching headers, content-type detection, etc.

To learn more about the Static Content API, please refer to the documentation.

Old API is still available and will be supported.

Support for 100 Continue for the CIO

The CIO engine now supports the 100 Continue status. It works out of the box and requires no additional configuration.

Multiple Configuration File Support

Configuration management has been simplified by allowing the use of multiple files. You can pass multiple configuration files using -config CLI option. The configuration files will be loaded and merged in order.

Please read more about the Configuration Files in the documentation.

Jetty 11 and Tomcat 10 Support

For projects already containing Jetty or Tomcat dependencies with major updates, Ktor 2.3.0 adds support for Jetty 11 and Tomcat 10 server implementations. They are delivered as separate dependencies:

dependencies {

If you need to use the new version, please change the dependency to the jakarta version. No migration is required.


Expect header and 100 Continue

The Expect header is now supported by the client. If you want to use it, please add Expect header to the request:

client.post("http://localhost:8080/upload") {
    header("Expect", "100-continue")
    body = "Hello, World!"

Sensitive Header Sanitizing in Logging Plugin

The Logging plugin now supports sanitizing sensitive headers. It can be configured using sanitizeHeader function:

val client = HttpClient {
    install(Logging) {
        sanitizeHeader("<secret>") { it == HttpHeaders.Authorization }

This configuration will replace the header value with a placeholder for all Authorization headers.

Static Linking of libcurl for mingwx64 Target

To simplify version management, libcurl library has been linked statically to ktor-client-curl. Installing libcurl on the system is no longer required to use the dependency.

Apache 5 and Jetty 11 Support

The new versions of Apache and Jetty were added as separate dependencies:

dependencies {

If you need to use the new version, please change the dependencies accordingly. No migration is required.

New Project Wizard and Sample Updates

The New Project Wizard has been updated with new Exposed and Postgres plugins, and new samples have been added to facilitate a more engaging Ktor experience.

The full list of changes can be found on GitHub.

Special Thanks to Our Contributors

A huge thank you to all contributors for their time, effort, and expertise in improving Ktor. Special thanks to chedabob, ay4toh5i, lkurcius, InsanusMokrassar, brianguertin, sproctor, nesk, Rattenkrieg, morki, pull-vert, Sebmaster.

Try it out

We invite you to discover the enhancements in Ktor 2.3.0 and share your invaluable feedback with us. If you are new to Ktor, you can create a new project using our New Project Generator. For reporting issues or suggesting new features, please visit YouTrack.

Thank you for your continued support!