Ktor 3.4.0 Help

Sending responses

Ktor allows you to handle incoming requests and send responses inside route handlers. You can send different types of responses: plain text, HTML documents and templates, serialized data objects, and so on. You can also configure various response parameters, such as content type, headers, cookies, and the status code.

Inside a route handler, the following API is available for working with responses:

Set response payload

Plain text

To send plain text, use the call.respondText() function:

get("/") { call.respondText("Hello, world!") }

HTML

Ktor provides two main mechanisms for generating HTML responses:

  • Building HTML using the Kotlin HTML DSL.

  • Rendering templates using JVM template engines such as FreeMarker or Velocity.

Full HTML documents

To send full HTML documents built with Kotlin DSL, use the call.respondHtml() function:

get("/") { val name = "Ktor" call.respondHtml(HttpStatusCode.OK) { head { title { +name } } body { h1 { +"Hello from $name!" } } } }

Partial HTML fragments

If you need to return only a fragment of HTML, without wrapping it in <html>, <head>, or <body>, you can use call.respondHtmlFragment():

get("/fragment") { call.respondHtmlFragment(HttpStatusCode.Created) { div("fragment") { span { +"Created!" } } } } }

Templates

To send a template in a response, use the call.respond() function with a specific content:

get("/index") { val sampleUser = User(1, "John") call.respond(FreeMarkerContent("index.ftl", mapOf("user" to sampleUser))) }

You can also use the call.respondTemplate() function:

get("/index") { val sampleUser = User(1, "John") call.respondTemplate("index.ftl", mapOf("user" to sampleUser)) }

You can learn more from the Templating help section.

Object

To enable serialization of data objects in Ktor, you need to install the ContentNegotiation plugin and register a required converter (for example, JSON). Then, you can use the call.respond() function to pass a data object in a response:

routing { get("/customer/{id}") { val id: Int by call.parameters val customer: Customer = customerStorage.find { it.id == id }!! call.respond(customer)

For the full example, see json-kotlinx.

File

To respond to a client with the content of a file, you have two options:

  • For a file represented as a File object, use the call.respondFile() function.

  • For a file pointed by the given Path object, use the call.respond() function with the LocalPathContent class.

The example below shows how to send a file and make it downloadable by adding the Content-Disposition header:

import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.http.content.* import io.ktor.server.plugins.autohead.* import io.ktor.server.plugins.partialcontent.* import io.ktor.server.response.* import io.ktor.server.routing.* import java.io.File import java.nio.file.Path fun Application.main() { install(PartialContent) install(AutoHeadResponse) routing { get("/download") { val file = File("files/ktor_logo.png") call.response.header( HttpHeaders.ContentDisposition, ContentDisposition.Attachment.withParameter(ContentDisposition.Parameters.FileName, "ktor_logo.png") .toString() ) call.respondFile(file) } get("/downloadFromPath") { val filePath = Path.of("files/file.txt") call.response.header( HttpHeaders.ContentDisposition, ContentDisposition.Attachment.withParameter(ContentDisposition.Parameters.FileName, "file.txt") .toString() ) call.respond(LocalPathContent(filePath)) } }

Note that this sample uses two plugins:

  • PartialContent enables the server to respond to requests with the Range header and send only a portion of content.

  • AutoHeadResponse provides the ability to automatically respond to a HEAD request for every route that has a GET defined. This allows the client application to determine the file size by reading the Content-Length header value.

For the full code sample, see download-file.

Resource

You can serve a single resource from the classpath with the call.respondResource() method. This method accepts the path to the resource and sends a response constructed in the following way: it reads the response body from the resource stream, and derives the Content-Type header from the file extension.

The following example shows the method call in a route handler:

routing { get("/resource") { call.respondResource("public/index.html") } }

In the example above, since the resource extension is .html, the response will include the Content-Type: text/html header. For convenience, you can pass components of the resource location, namely relative path and package, separately through the first and second parameters. The following example resolves resources under the assets package based on the requested path:

get("/assets/{rest-path...}") { var path = call.parameters["rest-path"] if (path.isNullOrEmpty()) { path = "index.html" } try { call.respondResource(path, "assets") { application.log.info(this.contentType.toString()) } } catch (_: IllegalArgumentException) { call.respond(HttpStatusCode.NotFound) } }

If the requested path after the /assets prefix is empty or /, the handler uses the default index.html resource to respond. If no resource is found at the given path, IllegalArgumentException is thrown. The previous code snippet mimics a more general solution — serving resources from a package with the staticResources() method.

Raw payload

To send the raw body payload, use the call.respondBytes() function.

Set response parameters

Status code

To set a status code for a response, call the ApplicationResponse.status() function with a predefined status code value:

get("/") { call.response.status(HttpStatusCode.OK) }

You can also specify custom status values:

get("/") { call.response.status(HttpStatusCode(418, "I'm a tea pot")) }

Content type

With the ContentNegotiation plugin installed, Ktor chooses a content type automatically. If required, you can specify a content type manually by passing a corresponding parameter.

In the example below, the call.respondText() function accepts ContentType.Text.Plain as a parameter:

get("/") { call.respondText("Hello, world!", ContentType.Text.Plain, HttpStatusCode.OK) }

Headers

You can add headers to a response in several ways:

  • Modify the ApplicationResponse.headers collection:

    get("/") { call.response.headers.append(HttpHeaders.ETag, "7c876b7e") // For multiple values for the same header call.response.headers.appendAll("X-Custom-Header" to listOf("value1", "value2")) }
  • Use the ApplicationResponse.header() function:

    get("/") { call.response.header(HttpHeaders.ETag, "7c876b7e") }
  • Use convenience functions for specific headers, such as ApplicationResponse.etag, ApplicationResponse.link, and others.

    get("/") { call.response.etag("7c876b7e") }
  • Add custom headers by passing raw string names:

    get("/") { call.response.header("Custom-Header", "Some value") }

Cookies

To configure cookies sent in a response, use the ApplicationResponse.cookies property:

get("/") { call.response.cookies.append("yummy_cookie", "choco") }

Redirects

To generate a redirect response, use the call.respondRedirect() function:

get("/") { call.respondRedirect("/moved", permanent = true) } get("/moved") { call.respondText("Moved content") }
23 January 2026