diff --git a/index.js b/index.js
index c2f44654219b21ff828909d604f650c0609031e6..5294bf78c7b6f7ab9e9d1ce226c247257b37e361 100644
--- a/index.js
+++ b/index.js
@@ -1,5 +1,5 @@
 // Required modules
-const express = require('express');
+const koa = require('koa');
 
 // Check for required environment variables
 for (let env of [
@@ -13,39 +13,44 @@ for (let env of [
   }
 }
 
-// Create Express app
-const app = express();
-app.disable('x-powered-by');
-app.disable('etag');
+// Create Koa app
+const app = koa();
+const route = require("koa-route");
 
 /**
  * Parse request body.
  */
-app.use((req, res, next) => {
+app.use(next => {
   if (req.method === 'GET' || req.method === 'DELETE') {
-    return next();
+    yield next;
+    return;
   }
-  if (!req.is('application/json')) {
-    return res.status(400).json({
+  if (!this.headers['application/json']) {
+    this.status = 400;
+    this.body = {
       code: 400,
       message: 'invalid content type, should be application/json'
-    });
+    };
+    yield next;
+    return;
   }
 
   let rawData = '';
 
-  req.on('data', chunk => {
+  this.req.on('data', chunk => {
     rawData += chunk.toString();
   });
-  req.on('end', () => {
+  this.req.on('end', () => {
     try {
-      req.body = JSON.parse(rawData);
-      next();
+      this.req.body = JSON.parse(rawData);
+      yield next;
+      return;
     } catch (err) {
-      res.status(400).json({
+      this.body = {
         code: 400,
         message: 'invalid body data, should be a valid JSON string'
-      });
+      };
+      this.status = 400;
     }
   });
 });
@@ -54,22 +59,22 @@ app.use((req, res, next) => {
  * POST /scan
  * Scan the file specified in the S3 event in the body.
  */
-app.post('/scan', require('./routes/scan.js'));
+app.use(route.post('/scan', require('./routes/scan.js')));
 
 /**
  * GET /freshclam
  * Run freshclam to update the virus database.
  */
-app.get('/freshclam', require('./routes/freshclam.js'));
+app.use(route.get('/freshclam', require('./routes/freshclam.js')));
 
 /**
  * GET /health
  * Return a 200 OK response, so that Elastic Beanstalk can check if the server
  * is still online.
  */
-app.get('/health', (req, res, next) => {
+app.use(route.get('/health', (req, res, next) => {
   res.status(200).end();
-});
+}));
 
 // Listen on 8080
 app.listen(8080);
diff --git a/routes/freshclam.js b/routes/freshclam.js
index 957565715785233f185e55aa455501a71f79b985..a0b952a5e3fc1e13f84aba9b1bdf8ef2539d9f48 100644
--- a/routes/freshclam.js
+++ b/routes/freshclam.js
@@ -5,12 +5,16 @@ const freshClam = require('../lib/freshclam.js');
  * > GET /freshclam
  * Run freshclam to update the virus database.
  */
-module.exports = function freshclam (req, res, next) {
+module.exports = function* freshclam(next) {
   freshClam().then(() => {
-    res.status(200).end();
+    this.body = null;
+    this.status = 200;
+    yield;
   }).catch(err => {
     console.error('failed to update virus definitions');
     console.error(err);
-    res.status(500).end();
+    this.body = null;
+    this.status = 500;
+    yield;
   });
-};
+}