diff --git a/.gitignore b/.gitignore
index 2abed01f..766f41c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
 /vector/bundle.*
 /vector/emojione/
 /vector/config.json
+/vector/index.html
 /vector/olm.*
 .DS_Store
 npm-debug.log
diff --git a/package.json b/package.json
index 935350fb..3fb45661 100644
--- a/package.json
+++ b/package.json
@@ -35,13 +35,13 @@
     "build:dev": "npm run build:emojione && npm run build:css && npm run build:bundle:dev",
     "package": "scripts/package.sh",
     "start:emojione": "cpx \"node_modules/emojione/assets/svg/*\" vector/emojione/svg/ -w",
-    "start:js": "webpack -w --progress",
-    "start:js:prod": "NODE_ENV=production webpack -w --progress",
+    "start:js": "webpack -w --progress --no-cache-buster",
+    "start:js:prod": "NODE_ENV=production webpack -w --progress --no-cache-buster",
     "start:skins:css": "mkdirp build && catw \"src/skins/vector/css/**/*.css\" -o build/components.css",
     "//cache": "Note the -c 1 below due to https://code.google.com/p/chromium/issues/detail?id=508270",
     "start": "node scripts/babelcheck.js && parallelshell \"npm run start:emojione\" \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
     "start:prod": "parallelshell \"npm run start:emojione\" \"npm run start:js:prod\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
-    "clean": "rimraf build lib vector/olm.* vector/bundle.* vector/emojione",
+    "clean": "rimraf build lib vector/olm.* vector/bundle.* vector/emojione vector/index.html",
     "prepublish": "npm run build:compile",
     "test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false",
     "test:multi": "karma start"
@@ -93,6 +93,7 @@
     "emojione": "^2.2.3",
     "expect": "^1.16.0",
     "fs-extra": "^0.30.0",
+    "html-webpack-plugin": "^2.24.0",
     "http-server": "^0.8.4",
     "json-loader": "^0.5.3",
     "karma": "^0.13.22",
diff --git a/vector/index.html b/src/vector/index.html
similarity index 92%
rename from vector/index.html
rename to src/vector/index.html
index 8a536d61..73cdd2df 100644
--- a/vector/index.html
+++ b/src/vector/index.html
@@ -20,14 +20,16 @@
     <meta name="msapplication-TileImage" content="vector-icons/mstile-144x144.png">
     <meta name="msapplication-config" content="vector-icons/browserconfig.xml">
     <meta name="theme-color" content="#ffffff">
+    <% for(var i=0; i<htmlWebpackPlugin.files.css.length; i++) {%>
+       <link href="<%= htmlWebpackPlugin.files.css[i] %>" rel="stylesheet">
+    <% } %>
   </head>
   <body style="height: 100%;">
     <section id="matrixchat" style="height: 100%;"></section>
-    <!-- load olm, if possible. -->
-    <script src="olm.js"></script>
-    <script src="bundle.js"></script>
     <noscript>Sorry, Riot requires JavaScript to be enabled.</noscript>
-    <link rel="stylesheet" href="bundle.css">
+    <% for(var i=0; i<htmlWebpackPlugin.files.js.length; i++) {%>
+       <script src="<%= htmlWebpackPlugin.files.js[i] %>"></script>
+    <% } %>
     <img src="img/warning.svg" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
     <audio id="messageAudio">
         <source src="media/message.ogg" type="audio/ogg" />
diff --git a/webpack.config.js b/webpack.config.js
index 4d5747f6..bfd233b5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,6 +1,16 @@
 var path = require('path');
 var webpack = require('webpack');
 var ExtractTextPlugin = require("extract-text-webpack-plugin");
+var HtmlWebpackPlugin = require('html-webpack-plugin');
+
+var cachebuster = true;
+
+for (var i=0; i < process.argv.length; i++) {
+    var arg = process.argv[i];
+    if (arg == "--no-cache-buster") {
+        cachebuster = false;
+    }
+}
 
 module.exports = {
     entry: {
@@ -39,7 +49,7 @@ module.exports = {
     },
     output: {
         path: path.join(__dirname, "vector"),
-        filename: "[name].js",
+        filename: "[name]" + (cachebuster ? ".[chunkhash]" : "") + ".js",
         devtoolModuleFilenameTemplate: function(info) {
             // Reading input source maps gives only relative paths here for
             // everything. Until I figure out how to fix this, this is a
@@ -73,8 +83,21 @@ module.exports = {
             }
         }),
 
-        new ExtractTextPlugin("bundle.css", {
-            allChunks: true
+        new ExtractTextPlugin(
+            "[name]" + (cachebuster ? ".[contenthash]" : "") + ".css",
+            {
+                allChunks: true
+            }
+        ),
+
+        new HtmlWebpackPlugin({
+            template: './src/vector/index.html',
+
+            // we inject the links ourselves via the template, because
+            // HtmlWebpackPlugin wants to put the script tags either at the
+            // bottom of <head> or the bottom of <body>, and I'm a bit scared
+            // about moving them.
+            inject: false,
         }),
     ],
     devtool: 'source-map'