diff --git a/.travis/default.yml b/.travis/default.yml
index 1875748d6..471a2a7c4 100644
--- a/.travis/default.yml
+++ b/.travis/default.yml
@@ -22,5 +22,5 @@ elasticsearch:
   port: 9200
   pass: ''
 recaptcha:
-  siteKey: hima
-  secretKey: saku
+  site_key: hima
+  secret_key: saku
diff --git a/.travis/test.yml b/.travis/test.yml
index f311310c7..6a115d6ab 100644
--- a/.travis/test.yml
+++ b/.travis/test.yml
@@ -22,5 +22,5 @@ elasticsearch:
   port: 9200
   pass: ''
 recaptcha:
-  siteKey: hima
-  secretKey: saku
+  site_key: hima
+  secret_key: saku
diff --git a/docs/config.md b/docs/config.md
new file mode 100644
index 000000000..0e23e09ae
--- /dev/null
+++ b/docs/config.md
@@ -0,0 +1,55 @@
+``` yaml
+# サーバーのメンテナ情報
+maintainer:
+  # メンテナの名前
+  name:
+
+  # メンテナの連絡先(URLかmailto形式のURL)
+  url:
+
+# プライマリURL
+url:
+
+# セカンダリURL
+secondary_url:
+
+# 待受ポート
+port:
+
+# TLSの設定
+https:
+  # TLSを有効にするか否か
+  enable: false
+
+  key: null
+  cert: null
+  ca: null
+
+# MongoDBの設定
+mongodb:
+  host: localhost
+  port: 27017
+  db: misskey
+  user:
+  pass:
+
+# Redisの設定
+redis:
+  host: localhost
+  port: 6379
+  pass:
+
+# reCAPTCHAの設定
+recaptcha:
+  site_key:
+  secret_key:
+
+# ServiceWrokerの設定
+sw:
+  # VAPIDの公開鍵
+  public_key:
+
+  # VAPIDの秘密鍵
+  private_key:
+
+```
diff --git a/docs/setup.en.md b/docs/setup.en.md
index 5ad57d5ab..9c31e4f17 100644
--- a/docs/setup.en.md
+++ b/docs/setup.en.md
@@ -36,6 +36,15 @@ Note that Misskey uses following subdomains:
 Misskey requires reCAPTCHA tokens.
 Please visit https://www.google.com/recaptcha/intro/ and generate keys.
 
+*(optional)* Generating VAPID keys
+----------------------------------------------------------------
+If you want to enable ServiceWroker, you need to generate VAPID keys:
+
+``` shell
+npm install web-push -g
+web-push generate-vapid-keys
+```
+
 *3.* Install dependencies
 ----------------------------------------------------------------
 Please install and setup these softwares:
@@ -51,24 +60,6 @@ Please install and setup these softwares:
 
 *4.* Install Misskey
 ----------------------------------------------------------------
-There is **two ways** to install Misskey:
-
-### WAY 1) Using built code (recommended)
-We have the official release of Misskey.
-The built code is automatically pushed to https://github.com/syuilo/misskey/tree/release after the CI test succeeds.
-
-1. `git clone -b release git://github.com/syuilo/misskey.git`
-2. `cd misskey`
-3. `npm install`
-
-#### Update
-1. `git fetch`
-2. `git reset --hard origin/release`
-3. `npm install`
-
-### WAY 2) Using source code
-If you want to build Misskey manually, you can do it via the
-`build` command after download the source code of Misskey and install dependencies:
 
 1. `git clone -b master git://github.com/syuilo/misskey.git`
 2. `cd misskey`
diff --git a/docs/setup.ja.md b/docs/setup.ja.md
index 602fd9b6a..1e8bb553f 100644
--- a/docs/setup.ja.md
+++ b/docs/setup.ja.md
@@ -37,6 +37,15 @@ Misskeyは以下のサブドメインを使います:
 MisskeyはreCAPTCHAトークンを必要とします。
 https://www.google.com/recaptcha/intro/ にアクセスしてトークンを生成してください。
 
+*(オプション)* VAPIDキーペアの生成
+----------------------------------------------------------------
+ServiceWorkerを有効にする場合、VAPIDキーペアを生成する必要があります:
+
+``` shell
+npm install web-push -g
+web-push generate-vapid-keys
+```
+
 *3.* 依存関係をインストールする
 ----------------------------------------------------------------
 これらのソフトウェアをインストール・設定してください:
@@ -52,26 +61,6 @@ https://www.google.com/recaptcha/intro/ にアクセスしてトークンを生
 
 *4.* Misskeyのインストール
 ----------------------------------------------------------------
-Misskeyをインストールするには**2つの方法**があります:
-
-### 方法 1) ビルドされたコードを利用する (推奨)
-Misskeyには公式のリリースがあります。
-ビルドされたコードはCIテストに合格した後、自動で https://github.com/syuilo/misskey/tree/release にpushされています。
-
-1. `git clone -b release git://github.com/syuilo/misskey.git`
-2. `cd misskey`
-3. `npm install`
-
-#### アップデートするには:
-1. `git fetch`
-2. `git reset --hard origin/release`
-3. `npm install`
-
-### 方法 2) ソースコードを利用する
-> 注: この方法では正しくビルド・動作できることは保証されません。
-
-Misskeyを手動でビルドしたい場合は、Misskeyのソースコードと依存関係をインストールした後、
-`build`コマンドを用いることができます:
 
 1. `git clone -b master git://github.com/syuilo/misskey.git`
 2. `cd misskey`
diff --git a/src/api/common/push-sw.ts b/src/api/common/push-sw.ts
index 782a4a6a6..2993c760e 100644
--- a/src/api/common/push-sw.ts
+++ b/src/api/common/push-sw.ts
@@ -4,7 +4,11 @@ import Subscription from '../models/sw-subscription';
 import config from '../../conf';
 
 if (config.sw) {
-	push.setGCMAPIKey(config.sw.gcm_api_key);
+	// アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録
+	push.setVapidDetails(
+		config.maintainer.url,
+		config.sw.public_key,
+		config.sw.private_key);
 }
 
 export default async function(userId: mongo.ObjectID | string, type, body?) {
diff --git a/src/api/private/signup.ts b/src/api/private/signup.ts
index e24734f80..466c6a489 100644
--- a/src/api/private/signup.ts
+++ b/src/api/private/signup.ts
@@ -9,7 +9,7 @@ import generateUserToken from '../common/generate-native-user-token';
 import config from '../../conf';
 
 recaptcha.init({
-	secret_key: config.recaptcha.secretKey
+	secret_key: config.recaptcha.secret_key
 });
 
 const home = {
diff --git a/src/config.ts b/src/config.ts
index e8322d833..7237b666f 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -3,7 +3,6 @@
  */
 
 import * as fs from 'fs';
-import * as URL from 'url';
 import * as yaml from 'js-yaml';
 import isUrl = require('is-url');
 
@@ -23,7 +22,19 @@ export const path = process.env.NODE_ENV == 'test'
  * ユーザーが設定する必要のある情報
  */
 type Source = {
-	maintainer: string;
+	/**
+	 * メンテナ情報
+	 */
+	maintainer: {
+		/**
+		 * メンテナの名前
+		 */
+		name: string;
+		/**
+		 * メンテナの連絡先(URLかmailto形式のURL)
+		 */
+		url: string;
+	};
 	url: string;
 	secondary_url: string;
 	port: number;
@@ -52,8 +63,8 @@ type Source = {
 		pass: string;
 	};
 	recaptcha: {
-		siteKey: string;
-		secretKey: string;
+		site_key: string;
+		secret_key: string;
 	};
 	accesslog?: string;
 	accesses?: {
@@ -80,8 +91,8 @@ type Source = {
 	 * Service Worker
 	 */
 	sw?: {
-		gcm_sender_id: string;
-		gcm_api_key: string;
+		public_key: string;
+		private_key: string;
 	};
 };
 
@@ -114,14 +125,6 @@ export default function load() {
 	if (!isUrl(config.url)) urlError(config.url);
 	if (!isUrl(config.secondary_url)) urlError(config.secondary_url);
 
-	const url = URL.parse(config.url);
-	const head = url.host.split('.')[0];
-
-	if (head != 'misskey' && head != 'localhost') {
-		console.error(`プライマリドメインは、必ず「misskey」ドメインで始まっていなければなりません(現在の設定では「${head}」で始まっています)。例えば「https://misskey.xyz」「http://misskey.my.app.example.com」などが正しいプライマリURLです。`);
-		process.exit();
-	}
-
 	config.url = normalizeUrl(config.url);
 	config.secondary_url = normalizeUrl(config.secondary_url);
 
diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag
index 8300bd571..716d61cde 100644
--- a/src/web/app/ch/tags/channel.tag
+++ b/src/web/app/ch/tags/channel.tag
@@ -26,11 +26,11 @@
 		<hr>
 		<mk-channel-form if={ SIGNIN } channel={ channel } ref="form"/>
 		<div if={ !SIGNIN }>
-			<p>参加するには<a href={ CONFIG.url }>ログインまたは新規登録</a>してください</p>
+			<p>参加するには<a href={ _URL_ }>ログインまたは新規登録</a>してください</p>
 		</div>
 		<hr>
 		<footer>
-			<small><a href={ CONFIG.url }>Misskey</a> ver { version } (葵 aoi)</small>
+			<small><a href={ _URL_ }>Misskey</a> ver { _VERSION_ } (葵 aoi)</small>
 		</footer>
 	</main>
 	<style>
@@ -66,7 +66,6 @@
 		this.channel = null;
 		this.posts = null;
 		this.connection = new ChannelStream(this.id);
-		this.version = VERSION;
 		this.unreadCount = 0;
 
 		this.on('mount', () => {
@@ -166,7 +165,7 @@
 <mk-channel-post>
 	<header>
 		<a class="index" onclick={ reply }>{ post.index }:</a>
-		<a class="name" href={ CONFIG.url + '/' + post.user.username }><b>{ post.user.name }</b></a>
+		<a class="name" href={ _URL_ + '/' + post.user.username }><b>{ post.user.name }</b></a>
 		<mk-time time={ post.created_at }/>
 		<mk-time time={ post.created_at } mode="detail"/>
 		<span>ID:<i>{ post.user.username }</i></span>
@@ -284,8 +283,6 @@
 
 	</style>
 	<script>
-		import CONFIG from '../../common/scripts/config';
-
 		this.mixin('api');
 
 		this.channel = this.opts.channel;
@@ -357,7 +354,7 @@
 				});
 			};
 
-			window.open(CONFIG.url + '/selectdrive?multiple=true',
+			window.open(_URL_ + '/selectdrive?multiple=true',
 				'drive_window',
 				'height=500,width=800');
 		};
@@ -390,7 +387,7 @@
 </mk-twitter-button>
 
 <mk-line-button>
-	<div class="line-it-button" data-lang="ja" data-type="share-a" data-url={ CONFIG.chUrl } style="display: none;"></div>
+	<div class="line-it-button" data-lang="ja" data-type="share-a" data-url={ _CH_URL_ } style="display: none;"></div>
 	<script>
 		this.on('mount', () => {
 			const head = document.getElementsByTagName('head')[0];
diff --git a/src/web/app/ch/tags/header.tag b/src/web/app/ch/tags/header.tag
index 5cdcbd09c..dec83c9a5 100644
--- a/src/web/app/ch/tags/header.tag
+++ b/src/web/app/ch/tags/header.tag
@@ -1,10 +1,10 @@
 <mk-header>
 	<div>
-		<a href={ CONFIG.chUrl }>Index</a> | <a href={ CONFIG.url }>Misskey</a>
+		<a href={ _CH_URL_ }>Index</a> | <a href={ _URL_ }>Misskey</a>
 	</div>
 	<div>
-		<a if={ !SIGNIN } href={ CONFIG.url }>ログイン(新規登録)</a>
-		<a if={ SIGNIN } href={ CONFIG.url + '/' + I.username }>{ I.username }</a>
+		<a if={ !SIGNIN } href={ _URL_ }>ログイン(新規登録)</a>
+		<a if={ SIGNIN } href={ _URL_ + '/' + I.username }>{ I.username }</a>
 	</div>
 	<style>
 		:scope
diff --git a/src/web/app/common/mios.ts b/src/web/app/common/mios.ts
index 96dac70f6..7f9aacc46 100644
--- a/src/web/app/common/mios.ts
+++ b/src/web/app/common/mios.ts
@@ -3,11 +3,12 @@ import * as riot from 'riot';
 import signout from './scripts/signout';
 import Progress from './scripts/loading';
 import HomeStreamManager from './scripts/streaming/home-stream-manager';
-import CONFIG from './scripts/config';
 import api from './scripts/api';
 
-declare var VERSION: string;
-declare var LANG: string;
+declare const _VERSION_: string;
+declare const _LANG_: string;
+declare const _API_URL_: string;
+declare const _SW_PUBLICKEY_: string;
 
 /**
  * Misskey Operating System
@@ -113,7 +114,7 @@ export default class MiOS extends EventEmitter {
 			}
 
 			// Fetch user
-			fetch(`${CONFIG.apiUrl}/i`, {
+			fetch(`${_API_URL_}/i`, {
 				method: 'POST',
 				body: JSON.stringify({
 					i: token
@@ -229,10 +230,15 @@ export default class MiOS extends EventEmitter {
 			this.swRegistration = registration;
 
 			// Options of pushManager.subscribe
+			// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
 			const opts = {
 				// A boolean indicating that the returned push subscription
 				// will only be used for messages whose effect is made visible to the user.
-				userVisibleOnly: true
+				userVisibleOnly: true,
+
+				// A public key your push server will use to send
+				// messages to client apps via a push server.
+				applicationServerKey: urlBase64ToUint8Array(_SW_PUBLICKEY_)
 			};
 
 			// Subscribe push notification
@@ -257,7 +263,7 @@ export default class MiOS extends EventEmitter {
 		});
 
 		// The path of service worker script
-		const sw = `/sw.${VERSION}.${LANG}.js`;
+		const sw = `/sw.${_VERSION_}.${_LANG_}.js`;
 
 		// Register service worker
 		navigator.serviceWorker.register(sw).then(registration => {
@@ -310,3 +316,22 @@ export default class MiOS extends EventEmitter {
 		});
 	}
 }
+
+/**
+ * Convert the URL safe base64 string to a Uint8Array
+ * @param base64String base64 string
+ */
+function urlBase64ToUint8Array(base64String: string): Uint8Array {
+	const padding = '='.repeat((4 - base64String.length % 4) % 4);
+	const base64 = (base64String + padding)
+		.replace(/\-/g, '+')
+		.replace(/_/g, '/');
+
+	const rawData = window.atob(base64);
+	const outputArray = new Uint8Array(rawData.length);
+
+	for (let i = 0; i < rawData.length; ++i) {
+		outputArray[i] = rawData.charCodeAt(i);
+	}
+	return outputArray;
+}
diff --git a/src/web/app/common/scripts/api.ts b/src/web/app/common/scripts/api.ts
index 5dcdb5971..e62447b0a 100644
--- a/src/web/app/common/scripts/api.ts
+++ b/src/web/app/common/scripts/api.ts
@@ -2,7 +2,7 @@
  * API Request
  */
 
-import CONFIG from './config';
+declare const _API_URL_: string;
 
 let spinner = null;
 let pending = 0;
@@ -26,7 +26,7 @@ export default (i, endpoint, data = {}): Promise<{ [x: string]: any }> => {
 
 	return new Promise((resolve, reject) => {
 		// Send request
-		fetch(endpoint.indexOf('://') > -1 ? endpoint : `${CONFIG.apiUrl}/${endpoint}`, {
+		fetch(endpoint.indexOf('://') > -1 ? endpoint : `${_API_URL_}/${endpoint}`, {
 			method: 'POST',
 			body: JSON.stringify(data),
 			credentials: endpoint === 'signin' ? 'include' : 'omit'
diff --git a/src/web/app/common/scripts/check-for-update.ts b/src/web/app/common/scripts/check-for-update.ts
index c1398ba54..c447a517f 100644
--- a/src/web/app/common/scripts/check-for-update.ts
+++ b/src/web/app/common/scripts/check-for-update.ts
@@ -1,12 +1,12 @@
 import MiOS from '../mios';
 
-declare var VERSION: string;
+declare const _VERSION_: string;
 
 export default async function(mios: MiOS) {
 	const meta = await mios.getMeta();
 
-	if (meta.version != VERSION) {
+	if (meta.version != _VERSION_) {
 		localStorage.setItem('should-refresh', 'true');
-		alert('%i18n:common.update-available%'.replace('{newer}', meta.version).replace('{current}', VERSION));
+		alert('%i18n:common.update-available%'.replace('{newer}', meta.version).replace('{current}', _VERSION_));
 	}
 }
diff --git a/src/web/app/common/scripts/config.ts b/src/web/app/common/scripts/config.ts
deleted file mode 100644
index b4801a44d..000000000
--- a/src/web/app/common/scripts/config.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-const _url = new URL(location.href);
-
-const isRoot = _url.host == 'localhost'
-	? true
-	: _url.host.split('.')[0] == 'misskey';
-
-const host = isRoot ? _url.host : _url.host.substring(_url.host.indexOf('.') + 1, _url.host.length);
-const scheme = _url.protocol;
-const url = `${scheme}//${host}`;
-const apiUrl = `${scheme}//api.${host}`;
-const chUrl = `${scheme}//ch.${host}`;
-const devUrl = `${scheme}//dev.${host}`;
-const aboutUrl = `${scheme}//about.${host}`;
-const statsUrl = `${scheme}//stats.${host}`;
-const statusUrl = `${scheme}//status.${host}`;
-
-export default {
-	host,
-	scheme,
-	url,
-	apiUrl,
-	chUrl,
-	devUrl,
-	aboutUrl,
-	statsUrl,
-	statusUrl
-};
diff --git a/src/web/app/common/scripts/signout.ts b/src/web/app/common/scripts/signout.ts
index 6c95cfbc9..292319654 100644
--- a/src/web/app/common/scripts/signout.ts
+++ b/src/web/app/common/scripts/signout.ts
@@ -1,7 +1,7 @@
-import CONFIG from './config';
+declare const _HOST_: string;
 
 export default () => {
 	localStorage.removeItem('me');
-	document.cookie = `i=; domain=.${CONFIG.host}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
+	document.cookie = `i=; domain=.${_HOST_}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
 	location.href = '/';
 };
diff --git a/src/web/app/common/scripts/streaming/stream.ts b/src/web/app/common/scripts/streaming/stream.ts
index 97ebdcdd7..770d77510 100644
--- a/src/web/app/common/scripts/streaming/stream.ts
+++ b/src/web/app/common/scripts/streaming/stream.ts
@@ -1,6 +1,7 @@
+declare const _API_URL_: string;
+
 import { EventEmitter } from 'eventemitter3';
 import * as ReconnectingWebsocket from 'reconnecting-websocket';
-import CONFIG from '../config';
 
 /**
  * Misskey stream connection
@@ -24,7 +25,7 @@ export default class Connection extends EventEmitter {
 		this.state = 'initializing';
 		this.buffer = [];
 
-		const host = CONFIG.apiUrl.replace('http', 'ws');
+		const host = _API_URL_.replace('http', 'ws');
 		const query = params
 			? Object.keys(params)
 				.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
diff --git a/src/web/app/common/scripts/text-compiler.ts b/src/web/app/common/scripts/text-compiler.ts
index 8c65d6a06..e0ea47df2 100644
--- a/src/web/app/common/scripts/text-compiler.ts
+++ b/src/web/app/common/scripts/text-compiler.ts
@@ -1,6 +1,7 @@
+declare const _URL_: string;
+
 import * as riot from 'riot';
 import * as pictograph from 'pictograph';
-import CONFIG from './config';
 
 const escape = text =>
 	text
@@ -26,7 +27,7 @@ export default (tokens, shouldBreak) => {
 			case 'link':
 				return `<a class="link" href="${escape(token.url)}" target="_blank" title="${escape(token.url)}">${escape(token.title)}</a>`;
 			case 'mention':
-				return `<a href="${CONFIG.url + '/' + escape(token.username)}" target="_blank" data-user-preview="${token.content}" ${me && me.username == token.username ? 'data-is-me' : ''}>${token.content}</a>`;
+				return `<a href="${_URL_ + '/' + escape(token.username)}" target="_blank" data-user-preview="${token.content}" ${me && me.username == token.username ? 'data-is-me' : ''}>${token.content}</a>`;
 			case 'hashtag': // TODO
 				return `<a>${escape(token.content)}</a>`;
 			case 'code':
diff --git a/src/web/app/common/tags/error.tag b/src/web/app/common/tags/error.tag
index 62f4563e5..51c2a6c13 100644
--- a/src/web/app/common/tags/error.tag
+++ b/src/web/app/common/tags/error.tag
@@ -170,8 +170,6 @@
 
 	</style>
 	<script>
-		import CONFIG from '../../common/scripts/config';
-
 		this.on('mount', () => {
 			this.update({
 				network: navigator.onLine
@@ -193,7 +191,7 @@
 				});
 
 				// Check misskey server is available
-				fetch(`${CONFIG.apiUrl}/meta`).then(() => {
+				fetch(`${_API_URL_}/meta`).then(() => {
 					this.update({
 						end: true,
 						server: true
diff --git a/src/web/app/common/tags/introduction.tag b/src/web/app/common/tags/introduction.tag
index fa1b1e247..3256688d1 100644
--- a/src/web/app/common/tags/introduction.tag
+++ b/src/web/app/common/tags/introduction.tag
@@ -3,7 +3,7 @@
 		<h1>Misskeyとは?</h1>
 		<p><ruby>Misskey<rt>みすきー</rt></ruby>は、<a href="http://syuilo.com" target="_blank">syuilo</a>が2014年くらいから<a href="https://github.com/syuilo/misskey" target="_blank">オープンソースで</a>開発・運営を行っている、ミニブログベースのSNSです。</p>
 		<p>無料で誰でも利用でき、広告も掲載していません。</p>
-		<p><a href={ CONFIG.aboutUrl } target="_blank">もっと知りたい方はこちら</a></p>
+		<p><a href={ _ABOUT_URL_ } target="_blank">もっと知りたい方はこちら</a></p>
 	</article>
 	<style>
 		:scope
diff --git a/src/web/app/common/tags/nav-links.tag b/src/web/app/common/tags/nav-links.tag
index b09e376b3..6043f128f 100644
--- a/src/web/app/common/tags/nav-links.tag
+++ b/src/web/app/common/tags/nav-links.tag
@@ -1,5 +1,5 @@
 <mk-nav-links>
-	<a href={ CONFIG.aboutUrl }>%i18n:common.tags.mk-nav-links.about%</a><i>・</i><a href={ CONFIG.statsUrl }>%i18n:common.tags.mk-nav-links.stats%</a><i>・</i><a href={ CONFIG.statusUrl }>%i18n:common.tags.mk-nav-links.status%</a><i>・</i><a href="http://zawazawa.jp/misskey/">%i18n:common.tags.mk-nav-links.wiki%</a><i>・</i><a href="https://github.com/syuilo/misskey/blob/master/DONORS.md">%i18n:common.tags.mk-nav-links.donors%</a><i>・</i><a href="https://github.com/syuilo/misskey">%i18n:common.tags.mk-nav-links.repository%</a><i>・</i><a href={ CONFIG.devUrl }>%i18n:common.tags.mk-nav-links.develop%</a><i>・</i><a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on <i class="fa fa-twitter"></i></a>
+	<a href={ _ABOUT_URL_ }>%i18n:common.tags.mk-nav-links.about%</a><i>・</i><a href={ _STATS_URL_ }>%i18n:common.tags.mk-nav-links.stats%</a><i>・</i><a href={ _STATUS_URL_ }>%i18n:common.tags.mk-nav-links.status%</a><i>・</i><a href="http://zawazawa.jp/misskey/">%i18n:common.tags.mk-nav-links.wiki%</a><i>・</i><a href="https://github.com/syuilo/misskey/blob/master/DONORS.md">%i18n:common.tags.mk-nav-links.donors%</a><i>・</i><a href="https://github.com/syuilo/misskey">%i18n:common.tags.mk-nav-links.repository%</a><i>・</i><a href={ _DEV_URL_ }>%i18n:common.tags.mk-nav-links.develop%</a><i>・</i><a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on <i class="fa fa-twitter"></i></a>
 	<style>
 		:scope
 			display inline
diff --git a/src/web/app/common/tags/signup.tag b/src/web/app/common/tags/signup.tag
index 17de0347f..6fec46ff3 100644
--- a/src/web/app/common/tags/signup.tag
+++ b/src/web/app/common/tags/signup.tag
@@ -3,7 +3,7 @@
 		<label class="username">
 			<p class="caption"><i class="fa fa-at"></i>%i18n:common.tags.mk-signup.username%</p>
 			<input ref="username" type="text" pattern="^[a-zA-Z0-9-]{3,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required="required" onkeyup={ onChangeUsername }/>
-			<p class="profile-page-url-preview" if={ refs.username.value != '' && username-state != 'invalidFormat' && username-state != 'minRange' && username-state != 'maxRange' }>{ CONFIG.url + '/' + refs.username.value }</p>
+			<p class="profile-page-url-preview" if={ refs.username.value != '' && username-state != 'invalidFormat' && username-state != 'minRange' && username-state != 'maxRange' }>{ _URL_ + '/' + refs.username.value }</p>
 			<p class="info" if={ usernameState == 'wait' } style="color:#999"><i class="fa fa-fw fa-spinner fa-pulse"></i>%i18n:common.tags.mk-signup.checking%</p>
 			<p class="info" if={ usernameState == 'ok' } style="color:#3CB7B5"><i class="fa fa-fw fa-check"></i>%i18n:common.tags.mk-signup.available%</p>
 			<p class="info" if={ usernameState == 'unavailable' } style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>%i18n:common.tags.mk-signup.unavailable%</p>
@@ -30,7 +30,7 @@
 		</label>
 		<label class="recaptcha">
 			<p class="caption"><i class="fa fa-toggle-on" if={ recaptchaed }></i><i class="fa fa-toggle-off" if={ !recaptchaed }></i>%i18n:common.tags.mk-signup.recaptcha%</p>
-			<div if={ recaptcha } class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey={ recaptcha.siteKey }></div>
+			<div if={ recaptcha } class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey={ recaptcha.site_key }></div>
 		</label>
 		<label class="agree-tou">
 			<input name="agree-tou" type="checkbox" autocomplete="off" required="required"/>
@@ -193,20 +193,16 @@
 		};
 
 		this.on('mount', () => {
-			fetch('/config.json').then(res => {
-				res.json().then(conf => {
-					this.update({
-						recaptcha: {
-							siteKey: conf.recaptcha.siteKey
-						}
-					});
-
-					const head = document.getElementsByTagName('head')[0];
-					const script = document.createElement('script');
-					script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
-					head.appendChild(script);
-				});
+			this.update({
+				recaptcha: {
+					site_key: _RECAPTCHA_SITEKEY_
+				}
 			});
+
+			const head = document.getElementsByTagName('head')[0];
+			const script = document.createElement('script');
+			script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
+			head.appendChild(script);
 		});
 
 		this.onChangeUsername = () => {
diff --git a/src/web/app/common/tags/twitter-setting.tag b/src/web/app/common/tags/twitter-setting.tag
index 470426700..3b70505ba 100644
--- a/src/web/app/common/tags/twitter-setting.tag
+++ b/src/web/app/common/tags/twitter-setting.tag
@@ -1,10 +1,10 @@
 <mk-twitter-setting>
-	<p>%i18n:common.tags.mk-twitter-setting.description%<a href={ CONFIG.aboutUrl + '/link-to-twitter' } target="_blank">%i18n:common.tags.mk-twitter-setting.detail%</a></p>
+	<p>%i18n:common.tags.mk-twitter-setting.description%<a href={ _ABOUT_URL_ + '/link-to-twitter' } target="_blank">%i18n:common.tags.mk-twitter-setting.detail%</a></p>
 	<p class="account" if={ I.twitter } title={ 'Twitter ID: ' + I.twitter.user_id }>%i18n:common.tags.mk-twitter-setting.connected-to%: <a href={ 'https://twitter.com/' + I.twitter.screen_name } target="_blank">@{ I.twitter.screen_name }</a></p>
 	<p>
-		<a href={ CONFIG.apiUrl + '/connect/twitter' } target="_blank" onclick={ connect }>{ I.twitter ? '%i18n:common.tags.mk-twitter-setting.reconnect%' : '%i18n:common.tags.mk-twitter-setting.connect%' }</a>
+		<a href={ _API_URL_ + '/connect/twitter' } target="_blank" onclick={ connect }>{ I.twitter ? '%i18n:common.tags.mk-twitter-setting.reconnect%' : '%i18n:common.tags.mk-twitter-setting.connect%' }</a>
 		<span if={ I.twitter }> or </span>
-		<a href={ CONFIG.apiUrl + '/disconnect/twitter' } target="_blank" if={ I.twitter } onclick={ disconnect }>%i18n:common.tags.mk-twitter-setting.disconnect%</a>
+		<a href={ _API_URL_ + '/disconnect/twitter' } target="_blank" if={ I.twitter } onclick={ disconnect }>%i18n:common.tags.mk-twitter-setting.disconnect%</a>
 	</p>
 	<p class="id" if={ I.twitter }>Twitter ID: { I.twitter.user_id }</p>
 	<style>
@@ -25,8 +25,6 @@
 				color #8899a6
 	</style>
 	<script>
-		import CONFIG from '../scripts/config';
-
 		this.mixin('i');
 
 		this.form = null;
@@ -47,7 +45,7 @@
 
 		this.connect = e => {
 			e.preventDefault();
-			this.form = window.open(CONFIG.apiUrl + '/connect/twitter',
+			this.form = window.open(_API_URL_ + '/connect/twitter',
 				'twitter_connect_window',
 				'height=570,width=520');
 			return false;
@@ -55,7 +53,7 @@
 
 		this.disconnect = e => {
 			e.preventDefault();
-			window.open(CONFIG.apiUrl + '/disconnect/twitter',
+			window.open(_API_URL_ + '/disconnect/twitter',
 				'twitter_disconnect_window',
 				'height=570,width=520');
 			return false;
diff --git a/src/web/app/common/tags/uploader.tag b/src/web/app/common/tags/uploader.tag
index da97957a2..145339169 100644
--- a/src/web/app/common/tags/uploader.tag
+++ b/src/web/app/common/tags/uploader.tag
@@ -172,7 +172,7 @@
 			if (folder) data.append('folder_id', folder);
 
 			const xhr = new XMLHttpRequest();
-			xhr.open('POST', this.CONFIG.apiUrl + '/drive/files/create', true);
+			xhr.open('POST', _API_URL_ + '/drive/files/create', true);
 			xhr.onload = e => {
 				const driveFile = JSON.parse(e.target.response);
 
diff --git a/src/web/app/desktop/scripts/fuck-ad-block.ts b/src/web/app/desktop/scripts/fuck-ad-block.ts
index 3307ba2f3..8be3c80ea 100644
--- a/src/web/app/desktop/scripts/fuck-ad-block.ts
+++ b/src/web/app/desktop/scripts/fuck-ad-block.ts
@@ -1,7 +1,7 @@
 require('fuckadblock');
 import dialog from './dialog';
 
-declare var fuckAdBlock: any;
+declare const fuckAdBlock: any;
 
 export default () => {
 	if (fuckAdBlock === undefined) {
diff --git a/src/web/app/desktop/scripts/update-avatar.ts b/src/web/app/desktop/scripts/update-avatar.ts
index 5fd7f2d3d..356f4e6f9 100644
--- a/src/web/app/desktop/scripts/update-avatar.ts
+++ b/src/web/app/desktop/scripts/update-avatar.ts
@@ -1,5 +1,6 @@
+declare const _API_URL_: string;
+
 import * as riot from 'riot';
-import CONFIG from '../../common/scripts/config';
 import dialog from './dialog';
 import api from '../../common/scripts/api';
 
@@ -44,7 +45,7 @@ export default (I, cb, file = null) => {
 		if (folder) data.append('folder_id', folder.id);
 
 		const xhr = new XMLHttpRequest();
-		xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true);
+		xhr.open('POST', _API_URL_ + '/drive/files/create', true);
 		xhr.onload = e => {
 			const file = JSON.parse((e.target as any).response);
 			progress.close();
diff --git a/src/web/app/desktop/scripts/update-banner.ts b/src/web/app/desktop/scripts/update-banner.ts
index 23a671c44..1996b7564 100644
--- a/src/web/app/desktop/scripts/update-banner.ts
+++ b/src/web/app/desktop/scripts/update-banner.ts
@@ -1,5 +1,6 @@
+declare const _API_URL_: string;
+
 import * as riot from 'riot';
-import CONFIG from '../../common/scripts/config';
 import dialog from './dialog';
 import api from '../../common/scripts/api';
 
@@ -44,7 +45,7 @@ export default (I, cb, file = null) => {
 		if (folder) data.append('folder_id', folder.id);
 
 		const xhr = new XMLHttpRequest();
-		xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true);
+		xhr.open('POST', _API_URL_ + '/drive/files/create', true);
 		xhr.onload = e => {
 			const file = JSON.parse((e.target as any).response);
 			progress.close();
diff --git a/src/web/app/desktop/tags/analog-clock.tag b/src/web/app/desktop/tags/analog-clock.tag
index 6cd7103c6..c0489d3fe 100644
--- a/src/web/app/desktop/tags/analog-clock.tag
+++ b/src/web/app/desktop/tags/analog-clock.tag
@@ -72,7 +72,7 @@
 				const length = Math.min(canvW, canvH) / 4;
 				const uv = new Vec2(Math.sin(angle), -Math.cos(angle));
 				ctx.beginPath();
-				ctx.strokeStyle = THEME_COLOR;
+				ctx.strokeStyle = _THEME_COLOR_;
 				ctx.lineWidth = 2;
 				ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5);
 				ctx.lineTo(canvW / 2 + uv.x * length,     canvH / 2 + uv.y * length);
diff --git a/src/web/app/desktop/tags/drive/browser-window.tag b/src/web/app/desktop/tags/drive/browser-window.tag
index b5f368f1f..7cd24fc4a 100644
--- a/src/web/app/desktop/tags/drive/browser-window.tag
+++ b/src/web/app/desktop/tags/drive/browser-window.tag
@@ -28,8 +28,6 @@
 
 	</style>
 	<script>
-		import CONFIG from '../../../common/scripts/config';
-
 		this.mixin('api');
 
 		this.folder = this.opts.folder ? this.opts.folder : null;
@@ -37,9 +35,9 @@
 		this.popout = () => {
 			const folder = this.refs.window.refs.browser.folder;
 			if (folder) {
-				return `${CONFIG.url}/i/drive/folder/${folder.id}`;
+				return `${_URL_}/i/drive/folder/${folder.id}`;
 			} else {
-				return `${CONFIG.url}/i/drive`;
+				return `${_URL_}/i/drive`;
 			}
 		};
 
diff --git a/src/web/app/desktop/tags/home-widgets/broadcast.tag b/src/web/app/desktop/tags/home-widgets/broadcast.tag
index 00fef8374..6f4bb0756 100644
--- a/src/web/app/desktop/tags/home-widgets/broadcast.tag
+++ b/src/web/app/desktop/tags/home-widgets/broadcast.tag
@@ -114,8 +114,8 @@
 				let broadcasts = [];
 				if (meta.broadcasts) {
 					meta.broadcasts.forEach(broadcast => {
-						if (broadcast[LANG]) {
-							broadcasts.push(broadcast[LANG]);
+						if (broadcast[_LANG_]) {
+							broadcasts.push(broadcast[_LANG_]);
 						}
 					});
 				}
diff --git a/src/web/app/desktop/tags/home-widgets/channel.tag b/src/web/app/desktop/tags/home-widgets/channel.tag
index fc4a27e4f..f22a5f76e 100644
--- a/src/web/app/desktop/tags/home-widgets/channel.tag
+++ b/src/web/app/desktop/tags/home-widgets/channel.tag
@@ -193,7 +193,7 @@
 <mk-channel-post>
 	<header>
 		<a class="index" onclick={ reply }>{ post.index }:</a>
-		<a class="name" href={ CONFIG.url + '/' + post.user.username }><b>{ post.user.name }</b></a>
+		<a class="name" href={ _URL_ + '/' + post.user.username }><b>{ post.user.name }</b></a>
 		<span>ID:<i>{ post.user.username }</i></span>
 	</header>
 	<div>
diff --git a/src/web/app/desktop/tags/home-widgets/version.tag b/src/web/app/desktop/tags/home-widgets/version.tag
index 7a0ff09b1..2b66b0490 100644
--- a/src/web/app/desktop/tags/home-widgets/version.tag
+++ b/src/web/app/desktop/tags/home-widgets/version.tag
@@ -1,5 +1,5 @@
 <mk-version-home-widget>
-	<p>ver { version } (葵 aoi)</p>
+	<p>ver { _VERSION_ } (葵 aoi)</p>
 	<style>
 		:scope
 			display block
@@ -16,7 +16,5 @@
 	</style>
 	<script>
 		this.mixin('widget');
-
-		this.version = VERSION;
 	</script>
 </mk-version-home-widget>
diff --git a/src/web/app/desktop/tags/messaging/room-window.tag b/src/web/app/desktop/tags/messaging/room-window.tag
index dca0172be..1c6ff7c4b 100644
--- a/src/web/app/desktop/tags/messaging/room-window.tag
+++ b/src/web/app/desktop/tags/messaging/room-window.tag
@@ -19,11 +19,9 @@
 
 	</style>
 	<script>
-		import CONFIG from '../../../common/scripts/config';
-
 		this.user = this.opts.user;
 
-		this.popout = `${CONFIG.url}/i/messaging/${this.user.username}`;
+		this.popout = `${_URL_}/i/messaging/${this.user.username}`;
 
 		this.on('mount', () => {
 			this.refs.window.on('closed', () => {
diff --git a/src/web/app/desktop/tags/pages/entrance.tag b/src/web/app/desktop/tags/pages/entrance.tag
index 0dfca1f7a..824847f51 100644
--- a/src/web/app/desktop/tags/pages/entrance.tag
+++ b/src/web/app/desktop/tags/pages/entrance.tag
@@ -150,7 +150,7 @@
 </mk-entrance>
 
 <mk-entrance-signin>
-	<a class="help" href={ CONFIG.aboutUrl + '/help' } title="お困りですか?"><i class="fa fa-question"></i></a>
+	<a class="help" href={ _ABOUT_URL_ + '/help' } title="お困りですか?"><i class="fa fa-question"></i></a>
 	<div class="form">
 		<h1><img if={ user } src={ user.avatar_url + '?thumbnail&size=32' }/>
 			<p>{ user ? user.name : 'アカウント' }</p>
diff --git a/src/web/app/desktop/tags/timeline.tag b/src/web/app/desktop/tags/timeline.tag
index 3e745976f..13651dfa5 100644
--- a/src/web/app/desktop/tags/timeline.tag
+++ b/src/web/app/desktop/tags/timeline.tag
@@ -112,7 +112,7 @@
 			</header>
 			<div class="body">
 				<div class="text" ref="text">
-					<p class="channel" if={ p.channel != null }><a href={ CONFIG.chUrl + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p>
+					<p class="channel" if={ p.channel != null }><a href={ _CH_URL_ + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p>
 					<a class="reply" if={ p.reply }>
 						<i class="fa fa-reply"></i>
 					</a>
diff --git a/src/web/app/desktop/tags/ui.tag b/src/web/app/desktop/tags/ui.tag
index 908257620..047964fab 100644
--- a/src/web/app/desktop/tags/ui.tag
+++ b/src/web/app/desktop/tags/ui.tag
@@ -379,7 +379,7 @@
 	<ul>
 		<virtual if={ SIGNIN }>
 			<li class="home { active: page == 'home' }">
-				<a href={ CONFIG.url }>
+				<a href={ _URL_ }>
 					<i class="fa fa-home"></i>
 					<p>%i18n:desktop.tags.mk-ui-header-nav.home%</p>
 				</a>
@@ -393,7 +393,7 @@
 			</li>
 		</virtual>
 		<li class="ch">
-			<a href={ CONFIG.chUrl } target="_blank">
+			<a href={ _CH_URL_ } target="_blank">
 				<i class="fa fa-television"></i>
 				<p>%i18n:desktop.tags.mk-ui-header-nav.ch%</p>
 			</a>
diff --git a/src/web/app/init.ts b/src/web/app/init.ts
index 19605dc8a..76bad0ae6 100644
--- a/src/web/app/init.ts
+++ b/src/web/app/init.ts
@@ -2,13 +2,12 @@
  * App initializer
  */
 
-declare var VERSION: string;
-declare var LANG: string;
+declare const _VERSION_: string;
+declare const _LANG_: string;
+declare const _HOST_: string;
 
-import * as riot from 'riot';
 import checkForUpdate from './common/scripts/check-for-update';
 import mixin from './common/mixins';
-import CONFIG from './common/scripts/config';
 import MiOS from './common/mios';
 require('./common/tags');
 
@@ -16,15 +15,15 @@ require('./common/tags');
  * APP ENTRY POINT!
  */
 
-console.info(`Misskey v${VERSION} (葵 aoi)`);
+console.info(`Misskey v${_VERSION_} (葵 aoi)`);
 
-if (CONFIG.host != 'localhost') {
-	document.domain = CONFIG.host;
+if (_HOST_ != 'localhost') {
+	document.domain = _HOST_;
 }
 
 { // Set lang attr
 	const html = document.documentElement;
-	html.setAttribute('lang', LANG);
+	html.setAttribute('lang', _LANG_);
 }
 
 { // Set description meta tag
@@ -35,9 +34,6 @@ if (CONFIG.host != 'localhost') {
 	head.appendChild(meta);
 }
 
-// Set global configuration
-(riot as any).mixin({ CONFIG });
-
 // iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする
 try {
 	localStorage.setItem('kyoppie', 'yuppie');
@@ -94,7 +90,7 @@ function panic(e) {
 			+ '<hr>'
 			+ `<p>エラーコード: ${e.toString()}</p>`
 			+ `<p>ブラウザ バージョン: ${navigator.userAgent}</p>`
-			+ `<p>クライアント バージョン: ${VERSION}</p>`
+			+ `<p>クライアント バージョン: ${_VERSION_}</p>`
 			+ '<hr>'
 			+ '<p>問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。</p>'
 			+ '<p>Thank you for using Misskey.</p>'
diff --git a/src/web/app/mobile/tags/page/settings.tag b/src/web/app/mobile/tags/page/settings.tag
index b6501142e..95b3f757d 100644
--- a/src/web/app/mobile/tags/page/settings.tag
+++ b/src/web/app/mobile/tags/page/settings.tag
@@ -29,7 +29,7 @@
 	<ul>
 		<li><a onclick={ signout }><i class="fa fa-power-off"></i>%i18n:mobile.tags.mk-settings-page.signout%</a></li>
 	</ul>
-	<p><small>ver { version } (葵 aoi)</small></p>
+	<p><small>ver { _VERSION_ } (葵 aoi)</small></p>
 	<style>
 		:scope
 			display block
@@ -97,7 +97,5 @@
 		this.signout = signout;
 
 		this.mixin('i');
-
-		this.version = VERSION;
 	</script>
 </mk-settings>
diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag
index 1d6ce2359..074422a20 100644
--- a/src/web/app/mobile/tags/timeline.tag
+++ b/src/web/app/mobile/tags/timeline.tag
@@ -164,7 +164,7 @@
 			</header>
 			<div class="body">
 				<div class="text" ref="text">
-					<p class="channel" if={ p.channel != null }><a href={ CONFIG.chUrl + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p>
+					<p class="channel" if={ p.channel != null }><a href={ _CH_URL_ + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p>
 					<a class="reply" if={ p.reply }>
 						<i class="fa fa-reply"></i>
 					</a>
diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag
index 0c969d390..bad6bf73f 100644
--- a/src/web/app/mobile/tags/ui.tag
+++ b/src/web/app/mobile/tags/ui.tag
@@ -239,7 +239,7 @@
 				<li><a href="/i/messaging"><i class="fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="i fa fa-circle" if={ hasUnreadMessagingMessages }></i><i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
-				<li><a href={ CONFIG.chUrl } target="_blank"><i class="fa fa-television"></i>%i18n:mobile.tags.mk-ui-nav.ch%<i class="fa fa-angle-right"></i></a></li>
+				<li><a href={ _CH_URL_ } target="_blank"><i class="fa fa-television"></i>%i18n:mobile.tags.mk-ui-nav.ch%<i class="fa fa-angle-right"></i></a></li>
 				<li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
@@ -249,7 +249,7 @@
 				<li><a href="/i/settings"><i class="fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="fa fa-angle-right"></i></a></li>
 			</ul>
 		</div>
-		<a href={ CONFIG.aboutUrl }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a>
+		<a href={ _ABOUT_URL_ }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a>
 	</div>
 	<style>
 		:scope
diff --git a/src/web/app/stats/tags/index.tag b/src/web/app/stats/tags/index.tag
index 134fad3c0..4b5415b2f 100644
--- a/src/web/app/stats/tags/index.tag
+++ b/src/web/app/stats/tags/index.tag
@@ -4,7 +4,7 @@
 		<mk-users stats={ stats }/>
 		<mk-posts stats={ stats }/>
 	</main>
-	<footer><a href={ CONFIG.url }>{ CONFIG.host }</a></footer>
+	<footer><a href={ _URL_ }>{ _HOST_ }</a></footer>
 	<style>
 		:scope
 			display block
diff --git a/src/web/app/status/tags/index.tag b/src/web/app/status/tags/index.tag
index 8750942c6..cb379f66b 100644
--- a/src/web/app/status/tags/index.tag
+++ b/src/web/app/status/tags/index.tag
@@ -5,7 +5,7 @@
 		<mk-cpu-usage connection={ connection }/>
 		<mk-mem-usage connection={ connection }/>
 	</main>
-	<footer><a href={ CONFIG.url }>{ CONFIG.host }</a></footer>
+	<footer><a href={ _URL_ }>{ _HOST_ }</a></footer>
 	<style>
 		:scope
 			display block
diff --git a/src/web/server.ts b/src/web/server.ts
index 166e17b07..04de4fe5a 100644
--- a/src/web/server.ts
+++ b/src/web/server.ts
@@ -11,8 +11,6 @@ import * as bodyParser from 'body-parser';
 import * as favicon from 'serve-favicon';
 import * as compression from 'compression';
 
-import config from '../conf';
-
 /**
  * Init app
  */
@@ -50,29 +48,7 @@ app.get(/^\/sw\.(.+?)\.js$/, (req, res) => res.sendFile(`${__dirname}/assets/sw.
 /**
  * Manifest
  */
-app.get('/manifest.json', (req, res) => {
-	const manifest = require((`${__dirname}/assets/manifest.json`));
-
-	// Service Worker
-	if (config.sw) {
-		manifest['gcm_sender_id'] = config.sw.gcm_sender_id;
-	}
-
-	res.send(manifest);
-});
-
-/**
- * Serve config
- */
-app.get('/config.json', (req, res) => {
-	const conf = {
-		recaptcha: {
-			siteKey: config.recaptcha.siteKey
-		}
-	};
-
-	res.send(conf);
-});
+app.get('/manifest.json', (req, res) => res.sendFile(`${__dirname}/assets/manifest.json`));
 
 /**
  * Common API
diff --git a/webpack/module/rules/consts.ts b/webpack/module/rules/consts.ts
new file mode 100644
index 000000000..2469ecf34
--- /dev/null
+++ b/webpack/module/rules/consts.ts
@@ -0,0 +1,36 @@
+/**
+ * Replace consts
+ */
+
+const StringReplacePlugin = require('string-replace-webpack-plugin');
+
+import version from '../../../src/version';
+const constants = require('../../../src/const.json');
+import config from '../../../src/conf';
+
+export default lang => {
+	// 置換の誤爆を防ぐため文字数の多い順に並べてください
+	const consts = {
+		_RECAPTCHA_SITEKEY_: JSON.stringify(config.recaptcha.site_key),
+		_SW_PUBLICKEY_: JSON.stringify(config.sw.public_key),
+		_THEME_COLOR_: JSON.stringify(constants.themeColor),
+		_VERSION_: JSON.stringify(version),
+		_API_URL_: JSON.stringify(config.api_url),
+		_LANG_: JSON.stringify(lang),
+		_HOST_: JSON.stringify(config.host),
+		_URL_: JSON.stringify(config.url),
+	};
+
+	const replacements = Object.keys(consts).map(key => ({
+		pattern: new RegExp(key, 'g'), replacement: () => consts[key]
+	}));
+
+	return {
+		enforce: 'post',
+		test: /\.(tag|js|ts)$/,
+		exclude: /node_modules/,
+		loader: StringReplacePlugin.replace({
+			replacements: replacements
+		})
+	};
+};
diff --git a/webpack/module/rules/index.ts b/webpack/module/rules/index.ts
index 9c1262b3d..0006f622d 100644
--- a/webpack/module/rules/index.ts
+++ b/webpack/module/rules/index.ts
@@ -1,4 +1,5 @@
 import i18n from './i18n';
+import consts from './consts';
 import base64 from './base64';
 import themeColor from './theme-color';
 import tag from './tag';
@@ -7,6 +8,7 @@ import typescript from './typescript';
 
 export default (lang, locale) => [
 	i18n(lang, locale),
+	consts(lang),
 	base64(),
 	themeColor(),
 	tag(),
diff --git a/webpack/plugins/const.ts b/webpack/plugins/const.ts
deleted file mode 100644
index f64160b01..000000000
--- a/webpack/plugins/const.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Constant Replacer
- */
-
-import * as webpack from 'webpack';
-
-import version from '../../src/version';
-const constants = require('../../src/const.json');
-
-export default lang => new webpack.DefinePlugin({
-	VERSION: JSON.stringify(version),
-	LANG: JSON.stringify(lang),
-	THEME_COLOR: JSON.stringify(constants.themeColor)
-});
diff --git a/webpack/plugins/index.ts b/webpack/plugins/index.ts
index d37047b67..24782a1de 100644
--- a/webpack/plugins/index.ts
+++ b/webpack/plugins/index.ts
@@ -1,6 +1,5 @@
 const StringReplacePlugin = require('string-replace-webpack-plugin');
 
-import constant from './const';
 import hoist from './hoist';
 //import minify from './minify';
 import banner from './banner';
@@ -10,7 +9,6 @@ const isProduction = env === 'production';
 
 export default (version, lang) => {
 	const plugins = [
-		constant(lang),
 		new StringReplacePlugin(),
 		hoist()
 	];