Serving Static Content

Whether we’re creating a website or an HTTP endpoint, many applications need to serve files (such as stylesheets, scripts, images, etc.), While it is certainly possible with Ktor to load the contents of a file and send it in response to a request, given this is such a common functionality, Ktor simplifies the entire process for us with the static Feature.

The first step in defining a static route is to define the path under which the content should be served. For instance, if you want everything under the route assets to be treated as static content, you need to add the following to your application setup:

  1. routing {
  2. static("assets") {
  3. }
  4. }

The next step is to define where we want the content to be served from, which can be either

Folders

In order to serve the contents from a folder, we need to specify the folder name using the files function. The path is always relative to the application path:

  1. routing {
  2. static("assets") {
  3. files("css")
  4. }
  5. }

files("css") would then allow for any file located in the folder css to be served as static content under the given URL pattern, which in this case is assets. This means that a request to

/assets/stylesheet.css would serve the file /css/stylesheet.css

We can have as many folders as we like under a single path. For instance the following would also be valid:

  1. routing {
  2. static("assets") {
  3. files("css")
  4. files("js")
  5. }
  6. }

Serving individual files

In addition to serving files from folders, we can also specify individuals files we would like to make available by using the file function. Optionally this takes a second argument which allows us to map a physical filename to a virtual one:

  1. routing {
  2. static("static") {
  3. file("image.png")
  4. file("random.txt", "image.png")
  5. }
  6. }

Defining a default file

For a specific path, we can also define the default file to be loaded:

  1. routing {
  2. static("assets") {
  3. files("css")
  4. default("index.html")
  5. }
  6. }

which would cause a request to /assets/ to serve index.html.

Changing the default root folder

Ktor also provides us the ability to specify a different root folder from where contents is served. This is useful for instance if we want to dynamically define where contents should be served from, or even use absolute paths.

We can do this by setting the value of the staticRootFolder property:

  1. static("docs") {
  2. staticRootFolder = File("/system/folder/docs")
  3. files("public")
  4. }

which would then map any request to /docs to the physical folder /system/folder/docs/public.

Embedded Application Resources

We can embed content as resources in our applications and serve these using the resource and resources functions:

  1. static("assets") {
  2. resources("css")
  3. }

resources("css") would then allow for any file located under the resource css to be served as static content under the given URL pattern, which in this case is assets. This means that a request to

/assets/stylesheet.cs would serve the file /css/stylesheet.cs

We can have as many resources as we like under a single path. For instance the following would also be valid:

  1. routing {
  2. static("assets") {
  3. resources("css")
  4. resources("js")
  5. }
  6. }

Serving individual resources

In addition to serving files from resources, we can also specify individuals files we would like to make available by using the resource function. Optionally this takes a second argument which allows us to map a physical filename to a virtual one:

  1. routing {
  2. static("static") {
  3. resource("image.png")
  4. resource("random.txt", "image.png")
  5. }
  6. }

Defining a default resource

For a specific path, we can also define the default file to be loaded:

  1. routing {
  2. static("assets") {
  3. resources("css")
  4. defaultResource("index.html")
  5. }
  6. }

Changing the default resource package

Ktor also provides us the ability to specify a different base resource package from where contents is served.

We can do this by setting the value of the staticBasePackage property:

  1. static("docs") {
  2. staticBasePackage = File("/system/folder/docs")
  3. files("public")
  4. }

Sub-routes

If we want to have sub-routes, we can nest static functions:

  1. static("assets") {
  2. files("css")
  3. static("themes") {
  4. files("data")
  5. }
  6. }

allowing for /assets/themes to load files from the /data

Handling errors

If the request content is not found, Ktor will automatically respond with a 404 Not Found HTTP status code. For more information about personalising error handling, please see status pages

Customising Content Type header

Ktor automatically looks up the content type of a file based on its extension and sets the appropriate Content-Type header. The list of supported MIME types is defined in the mimelist.csv resource file located in ktor-server-core artifact.

Example

An example application that serves static files using both folders and resources can be found below:

  1. package io.ktor.snippets.staticcontent
  2. import io.ktor.application.*
  3. import io.ktor.features.*
  4. import io.ktor.html.*
  5. import io.ktor.http.content.*
  6. import io.ktor.routing.*
  7. import kotlinx.html.*
  8. import java.io.*
  9. fun Application.main() {
  10. install(DefaultHeaders)
  11. install(CallLogging)
  12. routing {
  13. routeFilesystem()
  14. routeResources()
  15. }
  16. }
  17. fun Route.routeFilesystem() {
  18. get("/") {
  19. call.respondHtml {
  20. head {
  21. title { +"Ktor: static-content" }
  22. styleLink("/static/styles.css")
  23. script(src = "/static/script.js") {}
  24. }
  25. body {
  26. p {
  27. +"Hello from Ktor static content sample application"
  28. }
  29. p {
  30. +"Current directory is ${System.getProperty("user.dir")}"
  31. }
  32. img(src = "static/image.png") {
  33. onClick = "message('clicked the image')"
  34. }
  35. }
  36. }
  37. }
  38. static("static") {
  39. // When running under IDEA make sure that working directory is set to this sample's project folder
  40. staticRootFolder = File("files")
  41. files("css")
  42. files("js")
  43. file("image.png")
  44. file("random.txt", "image.png")
  45. default("index.html")
  46. }
  47. static("custom") {
  48. staticRootFolder = File("/tmp") // Establishes a root folder
  49. files("public") // For this to work, make sure you have /tmp/public on your system
  50. static("themes") {
  51. // services /custom/themes
  52. files("data")
  53. }
  54. }
  55. }
  56. fun Route.routeResources() {
  57. get("/resources") {
  58. call.respondHtml {
  59. head {
  60. title { +"Ktor: static-content" }
  61. styleLink("/static-resources/styles.css")
  62. }
  63. body {
  64. p {
  65. +"Hello from Ktor static content served from resources, if the background is cornflowerblue"
  66. }
  67. }
  68. }
  69. }
  70. static("static-resources") {
  71. resources("css")
  72. }
  73. }