diff --git a/http-server.js b/http-server.js
index f4f63ff..b8239f2 100755
--- a/http-server.js
+++ b/http-server.js
@@ -1,9 +1,15 @@
 #!/usr/bin/env node
 // Standalone server for use without karma!
+var fs = require('fs')
 var http = require('http')
 var path = require('path')
-var factory = require('./middleware')
+var stack = require('stack')
+var static = require('serve-static')
+
 var cors = require('./cors')
+var logger = require('./logger')
+var mock = require('./mock-response')
+var factory = require('./middleware')
 
 var config = {
   root: path.resolve(process.cwd(), process.env.GIT_HTTP_MOCK_SERVER_ROOT || '.'),
@@ -11,5 +17,17 @@ var config = {
   route: process.env.GIT_HTTP_MOCK_SERVER_ROUTE || '/'
 }
 
-var server = http.createServer(cors(factory(config)))
+var server = http.createServer(
+  cors(
+    stack(
+      logger.log(),
+      logger.handler('x-mock-response'),
+      mock(),
+      logger.handler('git-http-server'),
+      factory(config),
+      logger.handler('serve-static'),
+      static(config.root)
+    )
+  )
+)
 server.listen(process.env.GIT_HTTP_MOCK_SERVER_PORT || 8174)
diff --git a/logger.js b/logger.js
new file mode 100644
index 0000000..9f30c52
--- /dev/null
+++ b/logger.js
@@ -0,0 +1,32 @@
+var chalk = require('chalk')
+var onFinished = require('on-finished')
+
+function padHandler (str) {
+  return (str + '                         ').slice(0, 18)
+}
+
+function padMethod (str) {
+  return (str + '    ').slice(0, 7)
+}
+
+function log () {
+  return function (req, res, next) {
+    onFinished(res, function () {
+      var color = (res.statusCode === 401 || res.statusCode < 400) ? chalk.green : chalk.red
+      console.log(color(padHandler('[' + res.handler + '] ') + res.statusCode + ' ' + padMethod(req.method) + ' ' + req.url))
+    })
+    next()
+  }
+}
+
+function handler (label) {
+  return function (req, res, next) {
+    res.handler = label
+    next()
+  }
+}
+
+module.exports = {
+  log: log,
+  handler: handler
+}
diff --git a/middleware.js b/middleware.js
index 77b3d2c..556217e 100644
--- a/middleware.js
+++ b/middleware.js
@@ -47,7 +47,6 @@ function factory (config) {
     if (req.method === 'OPTIONS') {
       res.statusCode = 204
       res.end('')
-      console.log(chalk.green('[git-http-server] 204 ' + pad(req.method) + ' ' + req.url))
       return
     }
     if (!next) next = () => void(0)
@@ -56,7 +55,6 @@ function factory (config) {
     } catch (err) {
       res.statusCode = 404
       res.end(err.message + '\n')
-      console.log(chalk.red('[git-http-server] 404 ' + pad(req.method) + ' ' + req.url))
       return
     }
     if (gitdir == null) return next()
@@ -79,7 +77,6 @@ function factory (config) {
         res.statusMessage = 'Authorization Required'
         res.setHeader('WWW-Authenticate', 'Basic')
         res.end('Unauthorized' + '\n')
-        console.log(chalk.green('[git-http-server] 401 ' + pad(req.method) + ' ' + req.url))
         return
       }
       let valid = await htpasswd.authenticate({
@@ -94,7 +91,6 @@ function factory (config) {
         res.statusMessage = 'Authorization Required'
         res.setHeader('WWW-Authenticate', 'Basic')
         res.end('Bad credentials' + '\n')
-        console.log(chalk.green('[git-http-server] 401 ' + pad(req.method) + ' ' + req.url))
         return
       }
     }
@@ -103,12 +99,10 @@ function factory (config) {
       if (err) {
         res.statusCode = 500
         res.end(err + '\n')
-        console.log(chalk.red('[git-http-server] 500 ' + pad(req.method) + ' ' + req.url))
         return
       }
 
       res.setHeader('content-type', service.type)
-      console.log(chalk.green('[git-http-server] 200 ' + pad(req.method) + ' ' + req.url))
       // console.log('[git-http-server] ' + service.cmd + ' ' + service.args.concat(gitdir).join(' '))
       var ps = spawn(service.cmd, service.args.concat(gitdir))
       ps.stdout.pipe(service.createStream()).pipe(ps.stdin)
diff --git a/mock-response.js b/mock-response.js
new file mode 100644
index 0000000..266ee5a
--- /dev/null
+++ b/mock-response.js
@@ -0,0 +1,16 @@
+module.exports = function () {
+  return function (req, res, next) {
+    for (let header in req.headers) {
+      if (header.startsWith('x-mock-header-')) {
+        res.setHeader(header.replace('x-mock-header-', ''), req.headers[header])
+        next = null
+      }
+    }
+    if (next) {
+      return next()
+    } else {
+      res.statusCode = req.headers['x-mock-status-code']
+      res.end(req.headers['x-mock-body'])
+    }
+  }
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 33803e2..58590be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1775,6 +1775,16 @@
         }
       }
     },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
     "detect-indent": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz",
@@ -1826,12 +1836,22 @@
       "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
       "dev": true
     },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
     "electron-to-chromium": {
       "version": "1.3.62",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.62.tgz",
       "integrity": "sha512-x09ndL/Gjnuk3unlAyoGyUg3wbs4w/bXurgL7wL913vXHAOWmMhrLf1VNGRaMLngmadd5Q8gsV9BFuIr6rP+Xg==",
       "dev": true
     },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
     "env-ci": {
       "version": "1.7.2",
       "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-1.7.2.tgz",
@@ -1866,6 +1886,11 @@
         "es6-promise": "^4.0.3"
       }
     },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -1883,6 +1908,11 @@
       "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
       "dev": true
     },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
     "execa": {
       "version": "0.10.0",
       "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
@@ -2100,6 +2130,11 @@
         "map-cache": "^0.2.2"
       }
     },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
     "from2": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
@@ -2457,6 +2492,17 @@
       "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==",
       "dev": true
     },
+    "http-errors": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+      "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.0",
+        "statuses": ">= 1.4.0 < 2"
+      }
+    },
     "http-proxy-agent": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
@@ -3336,8 +3382,7 @@
     "ms": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
     "nanomatch": {
       "version": "1.2.13",
@@ -3473,6 +3518,14 @@
         "isobject": "^3.0.1"
       }
     },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
     "once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -3718,6 +3771,11 @@
         "protocols": "^1.4.0"
       }
     },
+    "parseurl": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
+    },
     "pascalcase": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -3831,6 +3889,11 @@
       "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=",
       "dev": true
     },
+    "range-parser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+    },
     "rc": {
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -4122,6 +4185,52 @@
         "semver": "^5.0.3"
       }
     },
+    "send": {
+      "version": "0.16.2",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+      "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.6.2",
+        "mime": "1.4.1",
+        "ms": "2.0.0",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.0",
+        "statuses": "~1.4.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "mime": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+          "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
+        }
+      }
+    },
+    "serve-static": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+      "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.2",
+        "send": "0.16.2"
+      }
+    },
     "set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -4151,6 +4260,11 @@
         }
       }
     },
+    "setprototypeof": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+    },
     "shebang-command": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -4432,6 +4546,11 @@
         "streamsearch": "~0.1.2"
       }
     },
+    "stack": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/stack/-/stack-0.1.0.tgz",
+      "integrity": "sha1-6SNZipvlHmF2gsshzxsoGKRJraI="
+    },
     "static-extend": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
@@ -4453,6 +4572,11 @@
         }
       }
     },
+    "statuses": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+      "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
+    },
     "stream-combiner2": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
@@ -4771,7 +4895,7 @@
     },
     "underscore": {
       "version": "1.4.4",
-      "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
       "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ="
     },
     "union-value": {
diff --git a/package.json b/package.json
index b630451..35199bf 100644
--- a/package.json
+++ b/package.json
@@ -38,9 +38,12 @@
     "htpasswd-js": "^1.0.2",
     "micro-cors": "^0.1.1",
     "minimisted": "^2.0.0",
-    "tree-kill": "^1.2.0",
+    "on-finished": "^2.3.0",
+    "serve-static": "^1.13.2",
     "ssh-keygen": "^0.4.2",
-    "ssh2": "^0.6.1"
+    "ssh2": "^0.6.1",
+    "stack": "^0.1.0",
+    "tree-kill": "^1.2.0"
   },
   "devDependencies": {
     "semantic-release": "15.1.7",