Generating HTTP Responses

Estimated reading time: 5 minutes

When handling routes, or directly intercepting the pipeline, you get a context with an ApplicationCall. That call contains a property called response that allows you to emit the response.

Also the call itself has some useful convenience properties and methods that interact with the response.

Table of contents:


When using the Routing feature, you can access the call property inside route handlers:

routing {
    get("/") {
        call.respondText("Request uri: ${call.request.uri}")

When intercepting requests, the lambda handler in intercept has the call property available too:

intercept(ApplicationCallPipeline.Call) { 
    if (call.request.uri == "/") {
        call.respondText("Test String")

Controlling the HTTP headers and the status

You can control how the response is generated, the http status, the headers, cookies, and the payload.

Remember that since HTTP requests an responses are non-seekable streams, once you start emitting the response payload/content, the status and the headers are emitted, and you won’t be able to modify neither the status or the headers/cookies.

As part of the response, you can get access to its internal context:

  • val call: ApplicationCall =
  • val pipeline: ApplicationSendPipeline = request.pipeline


  • val headers: ResponseHeaders = request.headers

Convenience cookies instance to set Set-Cookie headers:

  • val cookies: ResponseCookies = request.headers

Getting and changing the HTTP Status:

  • request.status(HttpStatusCode.OK) - Sets the HttpStatusCode to a predefined standard one
  • request.status(HttpStatusCode(418, "I'm a tea pot")) - Sets the HttpStatusCode to a custom status code
  • val status: HttpStatusCode? = request.status() - Gets the currently set HttpStatusCode if set

  • response.contentType(ContentType.Application.Json.withCharset(Charsets.UTF_8)) - Typed way for setting the Content-Type
  • response.contentType("application/json; charset=UTF-8") - Untyped way for setting the Content-Type header

Custom headers:

  • response.header("X-My-Header", "my value") - Appends a custom header
  • response.header("X-My-Times", 1000) - Appends a custom header
  • response.header("X-My-Times", 1000L) - Appends a custom header
  • response.header("X-My-Date", Instant.EPOCH) - Appends a custom header

Convenience methods to set headers usually set by the infrastructure:

  • response.etag("33a64df551425fcc55e4d42a148795d9f25f89d4") - Sets the ETag used for caching
  • response.lastModified( - Sets the Last-Modified header
  • response.contentLength(1024L) - Sets the Content-Length. This is generally automatically set when sending the payload
  • response.cacheControl(CacheControl.NoCache(CacheControl.Visibility.Private)) - Sets the Cache-Control header in a typed way
  • response.expires( - Sets the Expires header
  • response.contentRange(1024L until 2048L, 4096L) - Sets the Content-Range header (check the PartialContent feature)

HTTP/2 pushing and HTTP/1 Link header

The call supports pushing.

  • In HTTP/2 it uses the push feature
  • In HTTP/1.2 it adds the Link header as a hint
routing {
    get("/") {

Pushing allows to reduce the time between the request and the display of the page. But beware that sending beforehand content, might send content already cached by the client.


You can easily generate redirection responses with the respondRedirect method, to send 301 Moved Permanently or 302 Found redirects, with a Location header.

call.respondRedirect("/moved/here", permanent = true)

Sending response content

Sending generic content (compatible with Content negotiation):

  • call.respond(MyDataClass("hello", "world")) - Check the Content Negotiation section
  • call.respond(HttpStatusCode.NotFound, MyDataClass("hello", "world")) - Specifies a status code, and sends a payload in a single call. Check StatusPages

Sending plain text:

  • call.respondText("text") - Just a string with the body
  • call.respondText("p { background: red; }", contentType = ContentType.Text.CSS, status = HttpStatusCode.OK) { ... } - Sending a text specifying the ContentType, the HTTP Status and configuring the OutgoingContent
  • call.respondText { "string" } - Responding a string with a suspend provider
  • call.respondText(contentType = ..., status = ...) { "string" } - Responding a string with a suspend provider

Sending files:

  • call.respondFile(File("/path/to/file")) - Sends a file
  • call.respondFile(File("basedir"), "filename") { ... } - Send a file and configures the OutgoingContent

When sending files based on the request parameters, be specially careful validating and limiting the input.

Sending chunked content using a Writer:

  • call.respondWrite { write("hello"); write("world") } - Sends text using a writer. This is used with the HTML DSL
  • call.respondWrite(contentType = ..., status = ...) { write("hello"); write("world") } - Sends text using a writer and specifies a contentType and a status

To specify a default content type for the request:

  • call.defaultTextContentType(contentType: ContentType?): ContentType

The OutgoingContent interface for configuring responses:

class OutgoingContent {
    val contentType: ContentType? get() = null // * Specifies [ContentType] for this resource.
    val contentLength: Long? get() = null // Specifies content length in bytes for this resource. - If null, the resources will be sent as `Transfer-Encoding: chunked` 
    val status: HttpStatusCode? // Status code to set when sending this content
    val headers: Headers // Headers to set when sending this content
    fun <T : Any> getProperty(key: AttributeKey<T>): T? = extensionProperties?.getOrNull(key) // Gets an extension property for this content
    fun <T : Any> setProperty(key: AttributeKey<T>, value: T?) // Sets an extension property for this content

Content negotiation

When configuring plugins for content negotiation, the pipeline may accept additional types for the call.respond methods.

Sending HTML with the DSL

Ktor includes an optional feature to send HTML content using a DSL.

Sending HTML with FreeMarker

Ktor includes an optional feature to send HTML content using FreeMarker.

Sending JSON with data classes

Ktor includes an optional feature to send JSON content using Content negotiation.