diff --git a/gulpfile.ts b/gulpfile.ts
index 8ae0cd550d..df8382547d 100644
--- a/gulpfile.ts
+++ b/gulpfile.ts
@@ -9,6 +9,7 @@ import * as ts from 'gulp-typescript';
 const sourcemaps = require('gulp-sourcemaps');
 import tslint from 'gulp-tslint';
 const cssnano = require('gulp-cssnano');
+const stylus = require('gulp-stylus');
 import * as uglifyComposer from 'gulp-uglify/composer';
 import pug = require('gulp-pug');
 import * as rimraf from 'rimraf';
@@ -38,8 +39,6 @@ if (isDebug) {
 
 const constants = require('./src/const.json');
 
-require('./src/client/docs/gulpfile.ts');
-
 gulp.task('build', [
 	'build:ts',
 	'build:copy',
@@ -201,3 +200,10 @@ gulp.task('build:client:pug', [
 			}))
 			.pipe(gulp.dest('./built/client/app/'))
 );
+
+gulp.task('doc', () =>
+	gulp.src('./src/docs/**/*.styl')
+		.pipe(stylus())
+		.pipe((cssnano as any)())
+		.pipe(gulp.dest('./built/docs/assets/'))
+);
diff --git a/package-lock.json b/package-lock.json
index 5cbfa2c18d..1774203480 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -620,6 +620,11 @@
 				"@types/mime": "*"
 			}
 		},
+		"@types/showdown": {
+			"version": "1.7.5",
+			"resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-1.7.5.tgz",
+			"integrity": "sha512-uUSUP6XtyTclRzTH0NLkEIiEowxYXOWDeulpngrPltEceOmsGdhfrl8xr3D4QfJA7FuUUyHwFQuWWURLFg3hgg=="
+		},
 		"@types/single-line-log": {
 			"version": "1.1.0",
 			"resolved": "https://registry.npmjs.org/@types/single-line-log/-/single-line-log-1.1.0.tgz",
@@ -15107,6 +15112,129 @@
 				"jsonify": "~0.0.0"
 			}
 		},
+		"showdown": {
+			"version": "1.8.6",
+			"resolved": "https://registry.npmjs.org/showdown/-/showdown-1.8.6.tgz",
+			"integrity": "sha1-kepO47elRIqspoIKTifmkMatdxw=",
+			"requires": {
+				"yargs": "^10.0.3"
+			},
+			"dependencies": {
+				"ansi-regex": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+					"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+				},
+				"camelcase": {
+					"version": "4.1.0",
+					"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+					"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+				},
+				"cliui": {
+					"version": "4.1.0",
+					"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+					"integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+					"requires": {
+						"string-width": "^2.1.1",
+						"strip-ansi": "^4.0.0",
+						"wrap-ansi": "^2.0.0"
+					}
+				},
+				"find-up": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+					"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+					"requires": {
+						"locate-path": "^2.0.0"
+					}
+				},
+				"locate-path": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+					"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+					"requires": {
+						"p-locate": "^2.0.0",
+						"path-exists": "^3.0.0"
+					}
+				},
+				"os-locale": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+					"integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+					"requires": {
+						"execa": "^0.7.0",
+						"lcid": "^1.0.0",
+						"mem": "^1.1.0"
+					}
+				},
+				"p-limit": {
+					"version": "1.3.0",
+					"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+					"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+					"requires": {
+						"p-try": "^1.0.0"
+					}
+				},
+				"p-locate": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+					"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+					"requires": {
+						"p-limit": "^1.1.0"
+					}
+				},
+				"p-try": {
+					"version": "1.0.0",
+					"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+					"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
+				},
+				"path-exists": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+					"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+				},
+				"strip-ansi": {
+					"version": "4.0.0",
+					"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+					"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+					"requires": {
+						"ansi-regex": "^3.0.0"
+					}
+				},
+				"which-module": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+					"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+				},
+				"yargs": {
+					"version": "10.1.2",
+					"resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz",
+					"integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==",
+					"requires": {
+						"cliui": "^4.0.0",
+						"decamelize": "^1.1.1",
+						"find-up": "^2.1.0",
+						"get-caller-file": "^1.0.1",
+						"os-locale": "^2.0.0",
+						"require-directory": "^2.1.1",
+						"require-main-filename": "^1.0.1",
+						"set-blocking": "^2.0.0",
+						"string-width": "^2.0.0",
+						"which-module": "^2.0.0",
+						"y18n": "^3.2.1",
+						"yargs-parser": "^8.1.0"
+					}
+				},
+				"yargs-parser": {
+					"version": "8.1.0",
+					"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz",
+					"integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==",
+					"requires": {
+						"camelcase": "^4.1.0"
+					}
+				}
+			}
+		},
 		"shvl": {
 			"version": "1.3.1",
 			"resolved": "https://registry.npmjs.org/shvl/-/shvl-1.3.1.tgz",
diff --git a/package.json b/package.json
index 75a11704ec..672b4d29ad 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
 		"@types/request-promise-native": "1.0.15",
 		"@types/rimraf": "2.0.2",
 		"@types/seedrandom": "2.4.27",
+		"@types/showdown": "1.7.5",
 		"@types/single-line-log": "1.1.0",
 		"@types/speakeasy": "2.0.2",
 		"@types/tmp": "0.0.33",
@@ -179,6 +180,7 @@
 		"s-age": "1.1.2",
 		"sass-loader": "7.0.3",
 		"seedrandom": "2.4.3",
+		"showdown": "1.8.6",
 		"single-line-log": "1.1.2",
 		"speakeasy": "2.0.0",
 		"style-loader": "0.21.0",
diff --git a/src/client/docs/about.en.pug b/src/client/docs/about.en.pug
deleted file mode 100644
index 893d9dd6a1..0000000000
--- a/src/client/docs/about.en.pug
+++ /dev/null
@@ -1,3 +0,0 @@
-h1 About Misskey
-
-p Misskey is a mini blog SNS.
diff --git a/src/client/docs/about.ja.pug b/src/client/docs/about.ja.pug
deleted file mode 100644
index fec933b0c6..0000000000
--- a/src/client/docs/about.ja.pug
+++ /dev/null
@@ -1,3 +0,0 @@
-h1 Misskeyについて
-
-p MisskeyはミニブログSNSです。
diff --git a/src/client/docs/api.ja.pug b/src/client/docs/api.ja.pug
deleted file mode 100644
index 665cfdc4b8..0000000000
--- a/src/client/docs/api.ja.pug
+++ /dev/null
@@ -1,103 +0,0 @@
-h1 Misskey API
-
-p MisskeyはWeb APIを公開しており、様々な操作をプログラム上から行うことができます。
-p APIを自分のアカウントから利用する場合(自分のアカウントのみ操作したい場合)と、アプリケーションから利用する場合(不特定のアカウントを操作したい場合)とで利用手順が異なりますので、それぞれのケースについて説明します。
-
-section
-	h2 自分の所有するアカウントからAPIにアクセスする場合
-	p 「設定 > API」で、APIにアクセスするのに必要なAPIキーを取得してください。
-	p APIにアクセスする際には、リクエストにAPIキーを「i」というパラメータ名で含めます。
-	div.ui.info.warn: p %fa:exclamation-triangle%アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。
-	p APIの詳しい使用法は「Misskey APIの利用」セクションをご覧ください。
-
-section
-	h2 アプリケーションからAPIにアクセスする場合
-	p
-		| 直接ユーザーのAPIキーをアプリケーションが扱うのは危険なので、
-		| アプリケーションからAPIを利用する際には、アプリケーションとアプリケーションを利用するユーザーが結び付けられた専用のトークン(アクセストークン)をMisskeyに発行してもらい、
-		| そのトークンをリクエストのパラメータに含める必要があります。
-	div.ui.info: p %fa:info-circle%アクセストークンは、ユーザーが自分のアカウントにあなたのアプリケーションがアクセスすることを許可した場合のみ発行されます
-
-	p それでは、アクセストークンを取得するまでの流れを説明します。
-
-	section
-		h3 1.アプリケーションを登録する
-		p まず、あなたのアプリケーションやWebサービス(以後、あなたのアプリと呼びます)をMisskeyに登録します。
-		p
-			a(href=common.config.dev_url, target="_blank") デベロッパーセンター
-			| にアクセスし、「アプリ > アプリ作成」に進みます。
-			| フォームに必要事項を記入し、アプリを作成してください。フォームの記入欄の説明は以下の通りです:
-
-		table
-			thead
-				tr
-					th 名前
-					th 説明
-			tbody
-				tr
-					td アプリケーション名
-					td あなたのアプリの名称。
-				tr
-					td アプリの概要
-					td あなたのアプリの簡単な説明や紹介。
-				tr
-					td コールバックURL
-					td ユーザーが後述する認証フォームで認証を終えた際にリダイレクトするURLを設定できます。あなたのアプリがWebサービスである場合に有用です。
-				tr
-					td 権限
-					td あなたのアプリが要求する権限。ここで要求した機能だけがAPIからアクセスできます。
-
-		p 登録が済むとあなたのアプリのシークレットキーが入手できます。このシークレットキーは後で使用します。
-		div.ui.info.warn: p %fa:exclamation-triangle%アプリに成りすまされる可能性があるため、極力このシークレットキーは公開しないようにしてください。
-
-	section
-		h3 2.ユーザーに認証させる
-		p あなたのアプリを使ってもらうには、ユーザーにアカウントへのアクセスの許可をもらう必要があります。
-		p
-			| 認証セッションを開始するには、#{common.config.api_url}/auth/session/generate へパラメータに appSecret としてシークレットキーを含めたリクエストを送信します。
-			| リクエスト形式はJSONで、メソッドはPOSTです。
-			| レスポンスとして認証セッションのトークンや認証フォームのURLが取得できるので、認証フォームのURLをブラウザで表示し、ユーザーにフォームを提示してください。
-
-		p
-			| あなたのアプリがコールバックURLを設定している場合、
-			| ユーザーがあなたのアプリの連携を許可すると設定しているコールバックURLに token という名前でセッションのトークンが含まれたクエリを付けてリダイレクトします。
-
-		p
-			| あなたのアプリがコールバックURLを設定していない場合、ユーザーがあなたのアプリの連携を許可したことを(何らかの方法で(たとえばボタンを押させるなど))確認出来るようにしてください。
-
-	section
-		h3 3.ユーザーのアクセストークンを取得する
-		p ユーザーが連携を許可したら、#{common.config.api_url}/auth/session/userkey へ次のパラメータを含むリクエストを送信します:
-		table
-			thead
-				tr
-					th 名前
-					th 型
-					th 説明
-			tbody
-				tr
-					td appSecret
-					td string
-					td あなたのアプリのシークレットキー
-				tr
-					td token
-					td string
-					td セッションのトークン
-		p 上手くいけば、認証したユーザーのアクセストークンがレスポンスとして取得できます。おめでとうございます!
-
-	p アクセストークンが取得できたら、「ユーザーのアクセストークン+あなたのアプリのシークレットキーをsha256したもの」を「i」というパラメータでリクエストに含めると、APIにアクセスすることができます。
-
-	p 「i」パラメータの生成方法を擬似コードで表すと次のようになります:
-	pre: code
-		| const i = sha256(accessToken + secretKey);
-
-	p APIの詳しい使用法は「Misskey APIの利用」セクションをご覧ください。
-
-section
-	h2 Misskey APIの利用
-	p APIはすべてリクエストのパラメータ・レスポンスともにJSON形式です。また、すべてのエンドポイントはPOSTメソッドのみ受け付けます。
-	p APIリファレンスもご確認ください。
-
-	section
-		h3 レートリミット
-		p Misskey APIにはレートリミットがあり、短時間のうちに多数のリクエストを送信すると、一定時間APIを利用することができなくなることがあります。
diff --git a/src/client/docs/follow.ja.pug b/src/client/docs/follow.ja.pug
deleted file mode 100644
index f0e83bc8fd..0000000000
--- a/src/client/docs/follow.ja.pug
+++ /dev/null
@@ -1,9 +0,0 @@
-h1 フォロー
-p ユーザーをフォローすると、タイムラインにそのユーザーの投稿が表示されるようになります。ただし、他のユーザーに対する返信は含まれません。
-p ユーザーをフォローするには、ユーザーページの「フォロー」ボタンをクリックします。フォローを解除するには、もう一度クリックします。
-
-section
-	h2 ストーキング
-	p ユーザーをフォローしている状態では、さらに「ストーキング」モードをオンにすることができます。ストーキングを行うと、タイムラインにそのユーザーの全ての投稿が表示されるようになります。つまり、他のユーザーに対する返信も含まれることになります。
-	p ストーキングするには、ユーザーページの「ストークする」をクリックします。ストーキングをやめるには、もう一度クリックします。
-	p ストーキングしていることは相手に通知されません。
diff --git a/src/client/docs/gulpfile.ts b/src/client/docs/gulpfile.ts
deleted file mode 100644
index 2a95dfbfee..0000000000
--- a/src/client/docs/gulpfile.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Gulp tasks
- */
-
-import * as gulp from 'gulp';
-const stylus = require('gulp-stylus');
-const cssnano = require('gulp-cssnano');
-
-gulp.task('doc', [
-	'doc:styles'
-]);
-
-gulp.task('doc:styles', () =>
-	gulp.src('./src/client/docs/**/*.styl')
-		.pipe(stylus())
-		.pipe((cssnano as any)())
-		.pipe(gulp.dest('./built/client/docs/assets/'))
-);
diff --git a/src/client/docs/index.en.pug b/src/client/docs/index.en.pug
deleted file mode 100644
index 1fcc870d3d..0000000000
--- a/src/client/docs/index.en.pug
+++ /dev/null
@@ -1,3 +0,0 @@
-h1 Misskey Docs
-
-p Welcome to docs of Misskey.
diff --git a/src/client/docs/index.ja.pug b/src/client/docs/index.ja.pug
deleted file mode 100644
index 4a0bf7fa1d..0000000000
--- a/src/client/docs/index.ja.pug
+++ /dev/null
@@ -1,3 +0,0 @@
-h1 Misskey ドキュメント
-
-p Misskeyのドキュメントへようこそ
diff --git a/src/client/docs/license.en.pug b/src/client/docs/license.en.pug
deleted file mode 100644
index 45d8b76473..0000000000
--- a/src/client/docs/license.en.pug
+++ /dev/null
@@ -1,17 +0,0 @@
-h1 License
-
-div!= common.license
-
-details
-	summary Libraries
-
-	section
-		h2 Libraries
-
-		each dependency, name in common.dependencies
-			details
-				summary= name
-
-				section
-					h3= name
-					pre= dependency.licenseText
diff --git a/src/client/docs/license.ja.pug b/src/client/docs/license.ja.pug
deleted file mode 100644
index 6eb9ac308e..0000000000
--- a/src/client/docs/license.ja.pug
+++ /dev/null
@@ -1,17 +0,0 @@
-h1 ライセンス
-
-div!= common.license
-
-details
-	summary サードパーティ
-
-	section
-		h2 サードパーティ
-
-		each dependency, name in common.dependencies
-			details
-				summary= name
-
-				section
-					h3= name
-					pre= dependency.licenseText
diff --git a/src/client/docs/mute.ja.pug b/src/client/docs/mute.ja.pug
deleted file mode 100644
index 807f7b67a7..0000000000
--- a/src/client/docs/mute.ja.pug
+++ /dev/null
@@ -1,13 +0,0 @@
-h1 ミュート
-
-p ユーザーページから、そのユーザーをミュートすることができます。
-
-p ユーザーをミュートすると、そのユーザーに関する次のコンテンツがMisskeyに表示されなくなります:
-ul
-	li タイムラインや投稿の検索結果内の、そのユーザーの投稿(およびそれらの投稿に対する返信やRenote)
-	li そのユーザーからの通知
-	li メッセージ履歴一覧内の、そのユーザーとのメッセージ履歴
-
-p ミュートを行ったことは相手に通知されず、ミュートされていることを知ることもできません。
-
-p 設定>ミュート から、自分がミュートしているユーザー一覧を確認することができます。
diff --git a/src/client/docs/search.ja.pug b/src/client/docs/search.ja.pug
deleted file mode 100644
index fc62d16cae..0000000000
--- a/src/client/docs/search.ja.pug
+++ /dev/null
@@ -1,120 +0,0 @@
-h1 検索
-
-p 投稿を検索することができます。
-p
-	| キーワードを半角スペースで区切ると、and検索になります。
-	| 例えば、「git コミット」と検索すると、「gitで編集したファイルの特定の行だけコミットする方法がわからない」などがマッチします。
-
-section
-	h2 キーワードの除外
-	p キーワードの前に「-」(ハイフン)をプリフィクスすると、そのキーワードを含まない投稿に限定します。
-	p 例えば、「gitというキーワードを含むが、コミットというキーワードは含まない投稿」を検索したい場合、クエリは以下のようになります:
-	code git -コミット
-
-section
-	h2 完全一致
-	p テキストを「"""」で囲むと、そのテキストと完全に一致する投稿を検索します。
-	p 例えば、「"""にゃーん"""」と検索すると、「にゃーん」という投稿のみがヒットし、「にゃーん…」という投稿はヒットしません。
-
-section
-	h2 タグ
-	p キーワードの前に「#」(シャープ)をプリフィクスすると、そのキーワードと一致するタグを持つ投稿に限定します。
-
-section
-	h2 オプション
-	p
-		| オプションを使用して、より高度な検索を行えます。
-		| オプションを指定するには、「オプション名:値」という形式でクエリに含めます。
-	p 利用可能なオプション一覧です:
-
-	table
-		thead
-			tr
-				th 名前
-				th 説明
-		tbody
-			tr
-				td user
-				td
-					| 指定されたユーザー名のユーザーの投稿に限定します。
-					| 「,」(カンマ)で区切って、複数ユーザーを指定することもできます。
-					br
-					| 例えば、
-					code user:himawari,sakurako
-					| と検索すると「@himawariまたは@sakurakoの投稿」だけに限定します。
-					| (つまりユーザーのホワイトリストです)
-			tr
-				td exclude_user
-				td
-					| 指定されたユーザー名のユーザーの投稿を除外します。
-					| 「,」(カンマ)で区切って、複数ユーザーを指定することもできます。
-					br
-					| 例えば、
-					code exclude_user:akari,chinatsu
-					| と検索すると「@akariまたは@chinatsu以外の投稿」に限定します。
-					| (つまりユーザーのブラックリストです)
-			tr
-				td follow
-				td
-					| true ... フォローしているユーザーに限定。
-					br
-					| false ... フォローしていないユーザーに限定。
-					br
-					| null ... 特に限定しない(デフォルト)
-			tr
-				td mute
-				td
-					| mute_all ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteを除外する(デフォルト)
-					br
-					| mute_related ... ミュートしているユーザーの投稿に対する返信やRenoteだけ除外する
-					br
-					| mute_direct ... ミュートしているユーザーの投稿だけ除外する
-					br
-					| disabled ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteも含める
-					br
-					| direct_only ... ミュートしているユーザーの投稿だけに限定
-					br
-					| related_only ... ミュートしているユーザーの投稿に対する返信やRenoteだけに限定
-					br
-					| all_only ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteに限定
-			tr
-				td reply
-				td
-					| true ... 返信に限定。
-					br
-					| false ... 返信でない投稿に限定。
-					br
-					| null ... 特に限定しない(デフォルト)
-			tr
-				td renote
-				td
-					| true ... Renoteに限定。
-					br
-					| false ... Renoteでない投稿に限定。
-					br
-					| null ... 特に限定しない(デフォルト)
-			tr
-				td media
-				td
-					| true ... メディアが添付されている投稿に限定。
-					br
-					| false ... メディアが添付されていない投稿に限定。
-					br
-					| null ... 特に限定しない(デフォルト)
-			tr
-				td poll
-				td
-					| true ... 投票が添付されている投稿に限定。
-					br
-					| false ... 投票が添付されていない投稿に限定。
-					br
-					| null ... 特に限定しない(デフォルト)
-			tr
-				td until
-				td 上限の日時。(YYYY-MM-DD)
-			tr
-				td since
-				td 下限の日時。(YYYY-MM-DD)
-
-	p 例えば、「@syuiloの2017年11月1日から2017年12月31日までの『Misskey』というテキストを含む返信ではない投稿」を検索したい場合、クエリは以下のようになります:
-	code user:syuilo since:2017-11-01 until:2017-12-31 reply:false Misskey
diff --git a/src/client/docs/tou.ja.pug b/src/client/docs/tou.ja.pug
deleted file mode 100644
index 7663258f82..0000000000
--- a/src/client/docs/tou.ja.pug
+++ /dev/null
@@ -1,3 +0,0 @@
-h1 利用規約
-
-p 公序良俗に反する行為はおやめください。
diff --git a/src/docs/about.en.md b/src/docs/about.en.md
new file mode 100644
index 0000000000..bb1c51927b
--- /dev/null
+++ b/src/docs/about.en.md
@@ -0,0 +1,3 @@
+# About Misskey
+
+Misskey is a mini blog SNS.
diff --git a/src/docs/about.ja.md b/src/docs/about.ja.md
new file mode 100644
index 0000000000..1b06361f0f
--- /dev/null
+++ b/src/docs/about.ja.md
@@ -0,0 +1,3 @@
+# Misskeyについて
+
+MisskeyはミニブログSNSです。
diff --git a/src/client/docs/api/endpoints/style.styl b/src/docs/api/endpoints/style.styl
similarity index 100%
rename from src/client/docs/api/endpoints/style.styl
rename to src/docs/api/endpoints/style.styl
diff --git a/src/client/docs/api/endpoints/view.pug b/src/docs/api/endpoints/view.pug
similarity index 100%
rename from src/client/docs/api/endpoints/view.pug
rename to src/docs/api/endpoints/view.pug
diff --git a/src/client/docs/api/entities/drive-file.yaml b/src/docs/api/entities/drive-file.yaml
similarity index 100%
rename from src/client/docs/api/entities/drive-file.yaml
rename to src/docs/api/entities/drive-file.yaml
diff --git a/src/client/docs/api/entities/note.yaml b/src/docs/api/entities/note.yaml
similarity index 100%
rename from src/client/docs/api/entities/note.yaml
rename to src/docs/api/entities/note.yaml
diff --git a/src/client/docs/api/entities/post.yaml b/src/docs/api/entities/post.yaml
similarity index 100%
rename from src/client/docs/api/entities/post.yaml
rename to src/docs/api/entities/post.yaml
diff --git a/src/client/docs/api/entities/style.styl b/src/docs/api/entities/style.styl
similarity index 100%
rename from src/client/docs/api/entities/style.styl
rename to src/docs/api/entities/style.styl
diff --git a/src/client/docs/api/entities/user.yaml b/src/docs/api/entities/user.yaml
similarity index 100%
rename from src/client/docs/api/entities/user.yaml
rename to src/docs/api/entities/user.yaml
diff --git a/src/client/docs/api/entities/view.pug b/src/docs/api/entities/view.pug
similarity index 100%
rename from src/client/docs/api/entities/view.pug
rename to src/docs/api/entities/view.pug
diff --git a/src/client/docs/api/mixins.pug b/src/docs/api/mixins.pug
similarity index 100%
rename from src/client/docs/api/mixins.pug
rename to src/docs/api/mixins.pug
diff --git a/src/client/docs/api/style.styl b/src/docs/api/style.styl
similarity index 100%
rename from src/client/docs/api/style.styl
rename to src/docs/api/style.styl
diff --git a/src/docs/article.pug b/src/docs/article.pug
new file mode 100644
index 0000000000..fe68ceb910
--- /dev/null
+++ b/src/docs/article.pug
@@ -0,0 +1,4 @@
+extends ./layout.pug
+
+block main
+	!= html
diff --git a/src/client/docs/layout.pug b/src/docs/layout.pug
similarity index 100%
rename from src/client/docs/layout.pug
rename to src/docs/layout.pug
diff --git a/src/client/docs/style.styl b/src/docs/style.styl
similarity index 98%
rename from src/client/docs/style.styl
rename to src/docs/style.styl
index 3b13617588..d9ee9dad4a 100644
--- a/src/client/docs/style.styl
+++ b/src/docs/style.styl
@@ -1,4 +1,4 @@
-@import "../style"
+@import "../client/style"
 @import "./ui"
 
 body
diff --git a/src/client/docs/ui.styl b/src/docs/ui.styl
similarity index 100%
rename from src/client/docs/ui.styl
rename to src/docs/ui.styl
diff --git a/src/server/web/docs.ts b/src/server/web/docs.ts
index f4d6be885b..8f707d4d3f 100644
--- a/src/server/web/docs.ts
+++ b/src/server/web/docs.ts
@@ -4,6 +4,7 @@
 
 import * as fs from 'fs';
 import * as path from 'path';
+import * as showdown from 'showdown';
 import ms = require('ms');
 import * as Router from 'koa-router';
 import * as send from 'koa-send';
@@ -16,8 +17,6 @@ import { fa } from '../../misc/fa';
 import { licenseHtml } from '../../misc/license';
 const constants = require('../../const.json');
 
-const docs = `${__dirname}/../../client/docs/`;
-
 async function genVars(lang: string): Promise<{ [key: string]: any }> {
 	const vars = {} as { [key: string]: any };
 
@@ -26,23 +25,23 @@ async function genVars(lang: string): Promise<{ [key: string]: any }> {
 	const endpoints = glob.sync('./built/server/api/endpoints/**/*.js');
 	vars['endpoints'] = endpoints.map(ep => require('../../../' + ep)).filter(x => x.meta).map(x => x.meta.name);
 
-	const entities = glob.sync('./src/client/docs/api/entities/**/*.yaml');
+	const entities = glob.sync('./src/docs/api/entities/**/*.yaml');
 	vars['entities'] = entities.map(x => {
 		const _x = yaml.safeLoad(fs.readFileSync(x, 'utf-8')) as any;
 		return _x.name;
 	});
 
-	const docs = glob.sync('./src/client/docs/**/*.*.pug');
+	const docs = glob.sync('./src/docs/**/*.md');
 	vars['docs'] = {};
 	docs.forEach(x => {
-		const [, name, lang] = x.match(/docs\/(.+?)\.(.+?)\.pug$/);
+		const [, name, lang] = x.match(/docs\/(.+?)\.(.+?)\.md$/);
 		if (vars['docs'][name] == null) {
 			vars['docs'][name] = {
 				name,
 				title: {}
 			};
 		}
-		vars['docs'][name]['title'][lang] = fs.readFileSync(x, 'utf-8').match(/^h1 (.+?)\r?\n/)[1];
+		vars['docs'][name]['title'][lang] = fs.readFileSync(x, 'utf-8').match(/^# (.+?)\r?\n/)[1];
 	});
 
 	vars['kebab'] = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
@@ -162,7 +161,7 @@ const router = new Router();
 
 router.get('/assets/*', async ctx => {
 	await send(ctx, ctx.params[0], {
-		root: docs + '/assets/',
+		root: `${__dirname}/../../docs/assets/`,
 		maxage: ms('7 days'),
 		immutable: true
 	});
@@ -183,20 +182,20 @@ router.get('/*/api/endpoints/*', async ctx => {
 		// @ts-ignore
 		params: sortParams(Object.entries(ep.params).map(([k, v]) => parseParamDefinition(k, v))),
 		paramDefs: extractParamDefRef(Object.entries(ep.params).map(([k, v]) => v)),
-		res: ep.res.props ? sortParams(Object.entries(ep.res.props).map(([k, v]) => parsePropDefinition(k, v))) : null,
+		res: ep.res && ep.res.props ? sortParams(Object.entries(ep.res.props).map(([k, v]) => parsePropDefinition(k, v))) : null,
 		resDefs: null//extractPropDefRef(Object.entries(ep.res.props).map(([k, v]) => parsePropDefinition(k, v)))
 	};
 
-	await ctx.render('../../../../src/client/docs/api/endpoints/view', Object.assign(await genVars(lang), vars));
+	await ctx.render('../../../../src/docs/api/endpoints/view', Object.assign(await genVars(lang), vars));
 });
 
 router.get('/*/api/entities/*', async ctx => {
 	const lang = ctx.params[0];
 	const entity = ctx.params[1];
 
-	const x = yaml.safeLoad(fs.readFileSync(path.resolve('./src/client/docs/api/entities/' + entity + '.yaml'), 'utf-8')) as any;
+	const x = yaml.safeLoad(fs.readFileSync(path.resolve('./src/docs/api/entities/' + entity + '.yaml'), 'utf-8')) as any;
 
-	await ctx.render('../../../../src/client/docs/api/entities/view', Object.assign(await genVars(lang), {
+	await ctx.render('../../../../src/docs/api/entities/view', Object.assign(await genVars(lang), {
 		name: x.name,
 		desc: x.desc,
 		props: sortParams(Object.entries(x.props).map(([k, v]) => parsePropDefinition(k, v))),
@@ -208,7 +207,12 @@ router.get('/*/*', async ctx => {
 	const lang = ctx.params[0];
 	const doc = ctx.params[1];
 
-	await ctx.render('../../../../src/client/docs/' + doc + '.' + lang, await genVars(lang));
+	const conv = new showdown.Converter();
+	const md = fs.readFileSync(`${__dirname}/../../../src/docs/${doc}.${lang}.md`, 'utf8');
+
+	await ctx.render('../../../../src/docs/article', Object.assign({
+		html: conv.makeHtml(md)
+	}, await genVars(lang)));
 });
 
 export default router;