diff --git a/locales/ja.yml b/locales/ja.yml
index 06ef453de8..d8bc94b293 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -126,6 +126,8 @@ common/views/components/nav.vue:
 common/views/components/note-menu.vue:
   favorite: "お気に入り"
   pin: "ピン留め"
+  delete: "削除"
+  delete-confirm: "この投稿を削除しますか?"
   remote: "投稿元で見る"
 
 common/views/components/poll.vue:
@@ -360,14 +362,16 @@ desktop/views/components/messaging-window.vue:
 
 desktop/views/components/note-detail.vue:
   more: "会話をもっと読み込む"
-  private: "(この投稿は非公開です)"
+  private: "この投稿は非公開です"
+  deleted: "この投稿は削除されました"
   reposted-by: "{}がRenote"
   location: "位置情報"
   renote: "Renote"
   add-reaction: "リアクション"
 
 desktop/views/components/note-detail.sub.vue:
-  private: "(この投稿は非公開です)"
+  private: "この投稿は非公開です"
+  deleted: "この投稿は削除されました"
 
 desktop/views/components/notes.note.vue:
   reposted-by: "{}がRenote"
@@ -565,8 +569,9 @@ desktop/views/components/settings.profile.vue:
   is-cat: "このアカウントはCatです"
 
 desktop/views/components/sub-note-content.vue:
-  hidden: "(この投稿は非公開です)"
-  media: "つのメディア"
+  private: "この投稿は非公開です"
+  deleted: "この投稿は削除されました"
+  media-count: "{}つのメディア"
   poll: "投票"
 
 desktop/views/components/taskmanager.vue:
@@ -771,14 +776,16 @@ mobile/views/components/note.vue:
   reposted-by: "{}がRenote"
   more: "もっと見る"
   less: "隠す"
-  hidden: "この投稿は非公開です"
+  private: "この投稿は非公開です"
+  deleted: "この投稿は削除されました"
   location: "位置情報"
 
 mobile/views/components/note-detail.vue:
   reply: "返信"
   reaction: "リアクション"
   reposted-by: "{}がRenote"
-  hidden: "この投稿は非公開です"
+  private: "この投稿は非公開です"
+  deleted: "この投稿は削除されました"
   location: "位置情報"
 
 mobile/views/components/note-preview.vue:
@@ -813,8 +820,9 @@ mobile/views/components/post-form.vue:
   username-prompt: "ユーザー名を入力してください"
 
 mobile/views/components/sub-note-content.vue:
-  hidden: "この投稿は非公開です"
-  media-count: "{}個のメディア"
+  private: "この投稿は非公開です"
+  deleted: "この投稿は削除されました"
+  media-count: "{}つのメディア"
   poll: "投票"
 
 mobile/views/components/timeline.vue:
diff --git a/src/client/app/common/views/components/note-menu.vue b/src/client/app/common/views/components/note-menu.vue
index fb95055049..a400610a2b 100644
--- a/src/client/app/common/views/components/note-menu.vue
+++ b/src/client/app/common/views/components/note-menu.vue
@@ -4,6 +4,7 @@
 	<div class="popover" :class="{ compact }" ref="popover">
 		<button @click="favorite">%i18n:@favorite%</button>
 		<button v-if="note.userId == $store.state.i.id" @click="pin">%i18n:@pin%</button>
+		<button v-if="note.userId == $store.state.i.id" @click="del">%i18n:@delete%</button>
 		<a v-if="note.uri" :href="note.uri" target="_blank">%i18n:@remote%</a>
 	</div>
 </div>
@@ -59,6 +60,15 @@ export default Vue.extend({
 			});
 		},
 
+		del() {
+			if (!window.confirm('%i18n:@delete-confirm%')) return;
+			(this as any).api('notes/delete', {
+				noteId: this.note.id
+			}).then(() => {
+				this.$destroy();
+			});
+		},
+
 		favorite() {
 			(this as any).api('notes/favorites/create', {
 				noteId: this.note.id
diff --git a/src/client/app/desktop/views/components/note-detail.sub.vue b/src/client/app/desktop/views/components/note-detail.sub.vue
index 0471c70ee7..00e54ff1a6 100644
--- a/src/client/app/desktop/views/components/note-detail.sub.vue
+++ b/src/client/app/desktop/views/components/note-detail.sub.vue
@@ -16,6 +16,7 @@
 		<div class="body">
 			<div class="text">
 				<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+				<span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 				<mk-note-html v-if="note.text" :text="note.text" :i="$store.state.i"/>
 			</div>
 			<div class="media" v-if="note.mediaIds.length > 0">
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index e64990b4ce..63b2150110 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -39,6 +39,7 @@
 		<div class="body">
 			<div class="text">
 				<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+				<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 				<mk-note-html v-if="p.text" :text="p.text" :i="$store.state.i"/>
 			</div>
 			<div class="media" v-if="p.media.length > 0">
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index c4ad67c2f8..f293ffacfb 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -41,7 +41,8 @@
 				</p>
 				<div class="content" v-show="p.cw == null || showContent">
 					<div class="text">
-						<span v-if="p.isHidden" style="opacity: 0.5">(この投稿は非公開です)</span>
+						<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+						<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 						<a class="reply" v-if="p.reply">%fa:reply%</a>
 						<mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i" :class="$style.text"/>
 						<a class="rp" v-if="p.renote">RP:</a>
diff --git a/src/client/app/desktop/views/components/sub-note-content.vue b/src/client/app/desktop/views/components/sub-note-content.vue
index 8aa32cec73..46e1b802b9 100644
--- a/src/client/app/desktop/views/components/sub-note-content.vue
+++ b/src/client/app/desktop/views/components/sub-note-content.vue
@@ -1,13 +1,14 @@
 <template>
 <div class="mk-sub-note-content">
 	<div class="body">
-		<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@hidden%</span>
+		<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+		<span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 		<a class="reply" v-if="note.replyId">%fa:reply%</a>
 		<mk-note-html :text="note.text" :i="$store.state.i"/>
 		<a class="rp" v-if="note.renoteId" :href="`/note:${note.renoteId}`">RP: ...</a>
 	</div>
 	<details v-if="note.media.length > 0">
-		<summary>({{ note.media.length }}%i18n:@media%)</summary>
+		<summary>({{ '%i18n:@media-count%'.replace('{}', note.media.length) }})</summary>
 		<mk-media-list :media-list="note.media"/>
 	</details>
 	<details v-if="note.poll">
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index 91b8c20111..8ab766e1dc 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -36,7 +36,8 @@
 		</header>
 		<div class="body">
 			<div class="text">
-				<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@hidden%)</span>
+				<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+				<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 				<mk-note-html v-if="p.text" :text="p.text" :i="$store.state.i"/>
 			</div>
 			<div class="tags" v-if="p.tags && p.tags.length > 0">
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 35e1c24b07..997d83a6fe 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -41,7 +41,8 @@
 				</p>
 				<div class="content" v-show="p.cw == null || showContent">
 					<div class="text">
-						<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@hidden%)</span>
+						<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+						<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 						<a class="reply" v-if="p.reply">%fa:reply%</a>
 						<mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i" :class="$style.text"/>
 						<a class="rp" v-if="p.renote != null">RP:</a>
diff --git a/src/client/app/mobile/views/components/sub-note-content.vue b/src/client/app/mobile/views/components/sub-note-content.vue
index 023dec70d2..4ad90b97df 100644
--- a/src/client/app/mobile/views/components/sub-note-content.vue
+++ b/src/client/app/mobile/views/components/sub-note-content.vue
@@ -1,13 +1,14 @@
 <template>
 <div class="mk-sub-note-content">
 	<div class="body">
-		<span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@hidden%)</span>
+		<span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+		<span v-if="note.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 		<a class="reply" v-if="note.replyId">%fa:reply%</a>
 		<mk-note-html v-if="note.text" :text="note.text" :i="$store.state.i"/>
 		<a class="rp" v-if="note.renoteId">RP: ...</a>
 	</div>
 	<details v-if="note.media.length > 0">
-		<summary>({{ note.media.length }}個のメディア)</summary>
+		<summary>({{ '%i18n:@media-count%'.replace('{}', note.media.length) }})</summary>
 		<mk-media-list :media-list="note.media"/>
 	</details>
 	<details v-if="note.poll">
diff --git a/src/remote/activitypub/kernel/delete/note.ts b/src/remote/activitypub/kernel/delete/note.ts
index b2868f69a3..1951982f69 100644
--- a/src/remote/activitypub/kernel/delete/note.ts
+++ b/src/remote/activitypub/kernel/delete/note.ts
@@ -2,6 +2,7 @@ import * as debug from 'debug';
 
 import Note from '../../../../models/note';
 import { IRemoteUser } from '../../../../models/user';
+import deleteNode from '../../../../services/note/delete';
 
 const log = debug('misskey:activitypub');
 
@@ -18,12 +19,5 @@ export default async function(actor: IRemoteUser, uri: string): Promise<void> {
 		throw new Error('投稿を削除しようとしているユーザーは投稿の作成者ではありません');
 	}
 
-	Note.update({ _id: note._id }, {
-		$set: {
-			deletedAt: new Date(),
-			text: null,
-			mediaIds: [],
-			poll: null
-		}
-	});
+	await deleteNode(actor, note);
 }
diff --git a/src/remote/activitypub/renderer/delete.ts b/src/remote/activitypub/renderer/delete.ts
new file mode 100644
index 0000000000..d15cb447e6
--- /dev/null
+++ b/src/remote/activitypub/renderer/delete.ts
@@ -0,0 +1,4 @@
+export default object => ({
+	type: 'Delete',
+	object
+});
diff --git a/src/renderers/get-note-summary.ts b/src/renderers/get-note-summary.ts
index 643e2d09ba..ec7c74cf9f 100644
--- a/src/renderers/get-note-summary.ts
+++ b/src/renderers/get-note-summary.ts
@@ -3,6 +3,10 @@
  * @param {*} note (packされた)投稿
  */
 const summarize = (note: any): string => {
+	if (note.deletedAt) {
+		return '(削除された投稿)';
+	}
+
 	if (note.isHidden) {
 		return '(非公開の投稿)';
 	}
diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts
index 892da3540f..908d9574a5 100644
--- a/src/server/api/endpoints.ts
+++ b/src/server/api/endpoints.ts
@@ -494,6 +494,11 @@ const endpoints: Endpoint[] = [
 		},
 		kind: 'note-write'
 	},
+	{
+		name: 'notes/delete',
+		withCredential: true,
+		kind: 'note-write'
+	},
 	{
 		name: 'notes/renotes'
 	},
diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts
new file mode 100644
index 0000000000..9bbb1541d6
--- /dev/null
+++ b/src/server/api/endpoints/notes/delete.ts
@@ -0,0 +1,26 @@
+import $ from 'cafy'; import ID from '../../../../cafy-id';
+import Note from '../../../../models/note';
+import deleteNote from '../../../../services/note/delete';
+
+/**
+ * Delete a note
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+	// Get 'noteId' parameter
+	const [noteId, noteIdErr] = $.type(ID).get(params.noteId);
+	if (noteIdErr) return rej('invalid noteId param');
+
+	// Fetch note
+	const note = await Note.findOne({
+		_id: noteId,
+		userId: user._id
+	});
+
+	if (note === null) {
+		return rej('note not found');
+	}
+
+	await deleteNote(user, note);
+
+	res();
+});
diff --git a/src/services/note/delete.ts b/src/services/note/delete.ts
new file mode 100644
index 0000000000..793f0090be
--- /dev/null
+++ b/src/services/note/delete.ts
@@ -0,0 +1,44 @@
+import Note, { INote } from '../../models/note';
+import { IUser, isLocalUser } from '../../models/user';
+import { publishNoteStream } from '../../publishers/stream';
+import renderDelete from '../../remote/activitypub/renderer/delete';
+import pack from '../../remote/activitypub/renderer';
+import { deliver } from '../../queue';
+import Following from '../../models/following';
+import renderNote from '../../remote/activitypub/renderer/note';
+
+/**
+ * 投稿を削除します。
+ * @param user 投稿者
+ * @param note 投稿
+ */
+export default async function(user: IUser, note: INote) {
+	await Note.update({
+		_id: note._id,
+		userId: user._id
+	}, {
+		$set: {
+			deletedAt: new Date(),
+			text: null,
+			mediaIds: [],
+			poll: null
+		}
+	});
+
+	publishNoteStream(note._id, 'deleted');
+
+	//#region ローカルの投稿なら削除アクティビティを配送
+	if (isLocalUser(user)) {
+		const content = pack(renderDelete(await renderNote(note)));
+
+		const followings = await Following.find({
+			followeeId: user._id,
+			'_follower.host': { $ne: null }
+		});
+
+		followings.forEach(following => {
+			deliver(user, content, following._follower.inbox);
+		});
+	}
+	//#endregion
+}