diff --git a/docs/config/02-files.md b/docs/config/02-files.md
index 4c7debcb0..f85c4d772 100644
--- a/docs/config/02-files.md
+++ b/docs/config/02-files.md
@@ -31,7 +31,6 @@ Each pattern is either a simple string or an object with the following propertie
   * `css` - Include using `<link rel="stylesheet">` tag.
   * `html` - Include using [HTML Imports](https://developer.mozilla.org/en-US/docs/Web/Web_Components/HTML_Imports). Note that this feature is obsolete and does not work in the modern browsers. 
   * `js` - Include using `<script></script>` tag.
-  * `dart` - Include using `<script type="application/dart"></script>` tag. Note that this does not work in the modern browsers.
   * `module` - Include using `<script type="module"></script>` tag.
   * `dom` - Inline content of the file in the page. This can be used, for example, to test components combining HTML and JS.
 * **Description.** The type determines the mechanism for including the file.
diff --git a/docs/dev/05-plugins.md b/docs/dev/05-plugins.md
index d785102fb..d9371e46a 100644
--- a/docs/dev/05-plugins.md
+++ b/docs/dev/05-plugins.md
@@ -34,7 +34,7 @@ A preprocessor is a function that accepts three arguments (`content`, `file`, an
 - user NPM keywords `karma-plugin`, `karma-preprocessor`
 
 ## Crazier stuff
-Karma is assembled by Dependency Injection and a plugin is just an additional DI module (see [node-di] for more), that can be loaded by Karma. Therefore, it can ask for pretty much any Karma component and interact with it. There are a couple of plugins that do more interesting stuff like this, check out [karma-closure], [karma-intellij], [karma-dart].
+Karma is assembled by Dependency Injection and a plugin is just an additional DI module (see [node-di] for more), that can be loaded by Karma. Therefore, it can ask for pretty much any Karma component and interact with it. There are a couple of plugins that do more interesting stuff like this, check out [karma-closure], [karma-intellij].
 
 
 [karma-jasmine]: https://github.com/karma-runner/karma-jasmine
@@ -49,7 +49,6 @@ Karma is assembled by Dependency Injection and a plugin is just an additional DI
 [karma-ng-html2js-preprocessor]: https://github.com/karma-runner/karma-ng-html2js-preprocessor
 [karma-closure]: https://github.com/karma-runner/karma-closure
 [karma-intellij]: https://github.com/karma-runner/karma-intellij
-[karma-dart]: https://github.com/karma-runner/karma-dart
 [node-di]: https://github.com/vojtajina/node-di
 [karma-material-reporter]: https://github.com/ameerthehacker/karma-material-reporter
 
diff --git a/lib/middleware/karma.js b/lib/middleware/karma.js
index c48332d12..4e27d4a52 100644
--- a/lib/middleware/karma.js
+++ b/lib/middleware/karma.js
@@ -20,14 +20,12 @@ const common = require('./common')
 const VERSION = require('../constants').VERSION
 const SCRIPT_TYPE = {
   js: 'text/javascript',
-  dart: 'application/dart',
   module: 'module'
 }
 const FILE_TYPES = [
   'css',
   'html',
   'js',
-  'dart',
   'module',
   'dom'
 ]
diff --git a/lib/server.js b/lib/server.js
index d75c58a84..238caa09c 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -82,10 +82,6 @@ class Server extends KarmaEventEmitter {
       filesPromise: ['factory', createFilesPromise],
       socketServer: ['factory', createSocketIoServer],
       executor: ['factory', Executor.factory],
-      // TODO(vojta): remove
-      customFileHandlers: ['value', []],
-      // TODO(vojta): remove, once karma-dart does not rely on it
-      customScriptTypes: ['value', []],
       reporter: ['factory', reporter.createReporters],
       capturedBrowsers: ['factory', BrowserCollection.factory],
       args: ['value', {}],
diff --git a/lib/web-server.js b/lib/web-server.js
index a348f9421..379818435 100644
--- a/lib/web-server.js
+++ b/lib/web-server.js
@@ -16,17 +16,6 @@ const proxyMiddleware = require('./middleware/proxy')
 
 const log = require('./logger').create('web-server')
 
-function createCustomHandler (customFileHandlers, config) {
-  return function (request, response, next) {
-    const handler = customFileHandlers.find((handler) => handler.urlRegex.test(request.url))
-    return handler
-      ? handler.handler(request, response, 'fake/static', 'fake/adapter', config.basePath, 'fake/root')
-      : next()
-  }
-}
-
-createCustomHandler.$inject = ['customFileHandlers', 'config']
-
 function createFilesPromise (emitter, fileList) {
   // Set an empty list of files to avoid race issues with
   // file_list_modified not having been emitted yet
@@ -69,9 +58,6 @@ function createWebServer (injector, config) {
   handler.use(injector.invoke(sourceFilesMiddleware.create))
   // TODO(vojta): extract the proxy into a plugin
   handler.use(proxyMiddlewareInstance)
-  // TODO(vojta): remove, this is only here because of karma-dart
-  // we need a better way of custom handlers
-  handler.use(injector.invoke(createCustomHandler))
 
   if (config.middleware) {
     config.middleware.forEach((middleware) => handler.use(injector.get('middleware:' + middleware)))
diff --git a/test/unit/middleware/karma.spec.js b/test/unit/middleware/karma.spec.js
index 16242d09d..8a968262c 100644
--- a/test/unit/middleware/karma.spec.js
+++ b/test/unit/middleware/karma.spec.js
@@ -202,12 +202,12 @@ describe('middleware.karma', () => {
   it('should serve context.html with replaced script tags', (done) => {
     includedFiles([
       new MockFile('/first.js', 'sha123'),
-      new MockFile('/second.dart', 'sha456')
+      new MockFile('/second.js', 'sha456')
     ])
 
     response.once('end', () => {
       expect(nextSpy).not.to.have.been.called
-      expect(response).to.beServedAs(200, 'CONTEXT\n<script type="text/javascript" src="/__proxy__/__karma__/absolute/first.js?sha123" crossorigin="anonymous"></script>\n<script type="application/dart" src="/__proxy__/__karma__/absolute/second.dart?sha456" crossorigin="anonymous"></script>')
+      expect(response).to.beServedAs(200, 'CONTEXT\n<script type="text/javascript" src="/__proxy__/__karma__/absolute/first.js?sha123" crossorigin="anonymous"></script>\n<script type="text/javascript" src="/__proxy__/__karma__/absolute/second.js?sha456" crossorigin="anonymous"></script>')
       done()
     })
 
diff --git a/test/unit/web-server.spec.js b/test/unit/web-server.spec.js
index 5d9f4f18e..8517f172e 100644
--- a/test/unit/web-server.spec.js
+++ b/test/unit/web-server.spec.js
@@ -31,7 +31,7 @@ describe('web-server', () => {
   // NOTE(vojta): only loading once, to speed things up
   // this relies on the fact that none of these tests mutate fs
   const m = mocks.loadFile(path.join(__dirname, '/../../lib/web-server.js'), _mocks, _globals)
-  let customFileHandlers = server = emitter = null
+  server = emitter = null
   let beforeMiddlewareActive = false
   let middlewareActive = false
   const servedFiles = (files) => {
@@ -40,7 +40,6 @@ describe('web-server', () => {
 
   describe('request', () => {
     beforeEach(() => {
-      customFileHandlers = []
       emitter = new EventEmitter()
       const config = {
         basePath: '/base/path',
@@ -57,7 +56,6 @@ describe('web-server', () => {
 
       const injector = new di.Injector([{
         config: ['value', config],
-        customFileHandlers: ['value', customFileHandlers],
         emitter: ['value', emitter],
         fileList: ['value', { files: { served: [], included: [] } }],
         filesPromise: ['factory', m.createFilesPromise],
@@ -182,23 +180,6 @@ describe('web-server', () => {
         })
     })
 
-    it('should load custom handlers', () => {
-      servedFiles(new Set())
-
-      // TODO(vojta): change this, only keeping because karma-dart is relying on it
-      customFileHandlers.push({
-        urlRegex: /\/some\/weird/,
-        handler (request, response, staticFolder, adapterFolder, baseFolder, urlRoot) {
-          response.writeHead(222)
-          response.end('CONTENT')
-        }
-      })
-
-      return request(server)
-        .get('/some/weird/url')
-        .expect(222, 'CONTENT')
-    })
-
     it('should serve 404 for non-existing files', () => {
       servedFiles(new Set())
 
@@ -215,7 +196,6 @@ describe('web-server', () => {
         cert: fs.readFileSync(path.join(__dirname, '/certificates/server.crt'))
       }
 
-      customFileHandlers = []
       emitter = new EventEmitter()
 
       const injector = new di.Injector([{
@@ -226,7 +206,6 @@ describe('web-server', () => {
           httpsServerOptions: credentials,
           client: { useIframe: true, useSingleWindow: false }
         }],
-        customFileHandlers: ['value', customFileHandlers],
         emitter: ['value', emitter],
         fileList: ['value', { files: { served: [], included: [] } }],
         filesPromise: ['factory', m.createFilesPromise],
@@ -265,12 +244,10 @@ describe('web-server', () => {
         cert: fs.readFileSync(path.join(__dirname, '/certificates/server.crt'))
       }
 
-      customFileHandlers = []
       emitter = new EventEmitter()
 
       const injector = new di.Injector([{
         config: ['value', { basePath: '/base/path', urlRoot: '/', httpModule: http2, protocol: 'https:', httpsServerOptions: credentials }],
-        customFileHandlers: ['value', customFileHandlers],
         emitter: ['value', emitter],
         fileList: ['value', { files: { served: [], included: [] } }],
         filesPromise: ['factory', m.createFilesPromise],