diff --git a/src/remote/activitypub/act/index.ts b/src/remote/activitypub/act/index.ts
index 5fcdb61748..45d7bd16a9 100644
--- a/src/remote/activitypub/act/index.ts
+++ b/src/remote/activitypub/act/index.ts
@@ -1,9 +1,10 @@
+import { Object } from '../type';
+import { IRemoteUser } from '../../../models/user';
 import create from './create';
 import performDeleteActivity from './delete';
 import follow from './follow';
 import undo from './undo';
-import { Object } from '../type';
-import { IRemoteUser } from '../../../models/user';
+import like from './like';
 
 const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 	switch (activity.type) {
@@ -23,6 +24,10 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 		// noop
 		break;
 
+	case 'Like':
+		await like(actor, activity);
+		break;
+
 	case 'Undo':
 		await undo(actor, activity);
 		break;
@@ -33,7 +38,7 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 		break;
 
 	default:
-		console.warn(`unknown activity type: ${activity.type}`);
+		console.warn(`unknown activity type: ${(activity as any).type}`);
 		return null;
 	}
 };
diff --git a/src/remote/activitypub/act/like.ts b/src/remote/activitypub/act/like.ts
index ea53242017..2f5e3f807d 100644
--- a/src/remote/activitypub/act/like.ts
+++ b/src/remote/activitypub/act/like.ts
@@ -1,10 +1,10 @@
-import { MongoError } from 'mongodb';
-import Reaction, { IPostReaction } from '../../../models/post-reaction';
 import Post from '../../../models/post';
-import queue from '../../../queue';
+import { IRemoteUser } from '../../../models/user';
+import { ILike } from '../type';
+import create from '../../../services/post/reaction/create';
 
-export default async (resolver, actor, activity, distribute) => {
-	const id = activity.object.id || activity.object;
+export default async (actor: IRemoteUser, activity: ILike) => {
+	const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
 
 	// Transform:
 	// https://misskey.ex/@syuilo/xxxx to
@@ -16,48 +16,5 @@ export default async (resolver, actor, activity, distribute) => {
 		throw new Error();
 	}
 
-	if (!distribute) {
-		const { _id } = await Reaction.findOne({
-			userId: actor._id,
-			postId: post._id
-		});
-
-		return {
-			resolver,
-			object: { $ref: 'postPeactions', $id: _id }
-		};
-	}
-
-	const promisedReaction = Reaction.insert({
-		createdAt: new Date(),
-		userId: actor._id,
-		postId: post._id,
-		reaction: 'pudding'
-	}).then(reaction => new Promise<IPostReaction>((resolve, reject) => {
-		queue.create('http', {
-			type: 'reaction',
-			reactionId: reaction._id
-		}).save(error => {
-			if (error) {
-				reject(error);
-			} else {
-				resolve(reaction);
-			}
-		});
-	}), async error => {
-		// duplicate key error
-		if (error instanceof MongoError && error.code === 11000) {
-			return Reaction.findOne({
-				userId: actor._id,
-				postId: post._id
-			});
-		}
-
-		throw error;
-	});
-
-	return promisedReaction.then(({ _id }) => ({
-		resolver,
-		object: { $ref: 'postPeactions', $id: _id }
-	}));
+	await create(actor, post, 'pudding');
 };
diff --git a/src/remote/activitypub/renderer/like.ts b/src/remote/activitypub/renderer/like.ts
new file mode 100644
index 0000000000..903b10789e
--- /dev/null
+++ b/src/remote/activitypub/renderer/like.ts
@@ -0,0 +1,9 @@
+import config from '../../../config';
+
+export default (user, post) => {
+	return {
+		type: 'Like',
+		actor: `${config.url}/@${user.username}`,
+		object: post.uri ? post.uri : `${config.url}/posts/${post._id}`
+	};
+};
diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts
index b971a53951..bbab63db36 100644
--- a/src/remote/activitypub/renderer/note.ts
+++ b/src/remote/activitypub/renderer/note.ts
@@ -23,7 +23,7 @@ export default async (user: IUser, post: IPost) => {
 			});
 
 			if (inReplyToUser !== null) {
-				inReplyTo = inReplyToPost.uri || `${config.url}/@${inReplyToUser.username}/${inReplyToPost._id}`;
+				inReplyTo = inReplyToPost.uri || `${config.url}/posts/${inReplyToPost._id}`;
 			}
 		}
 	} else {
@@ -33,7 +33,7 @@ export default async (user: IUser, post: IPost) => {
 	const attributedTo = `${config.url}/@${user.username}`;
 
 	return {
-		id: `${attributedTo}/${post._id}`,
+		id: `${config.url}/posts/${post._id}`,
 		type: 'Note',
 		attributedTo,
 		content: post.textHtml,
diff --git a/src/remote/activitypub/type.ts b/src/remote/activitypub/type.ts
index c07b806be8..450d5906d8 100644
--- a/src/remote/activitypub/type.ts
+++ b/src/remote/activitypub/type.ts
@@ -55,6 +55,10 @@ export interface IAccept extends IActivity {
 	type: 'Accept';
 }
 
+export interface ILike extends IActivity {
+	type: 'Like';
+}
+
 export type Object =
 	ICollection |
 	IOrderedCollection |
@@ -62,4 +66,5 @@ export type Object =
 	IDelete |
 	IUndo |
 	IFollow |
-	IAccept;
+	IAccept |
+	ILike;
diff --git a/src/server/api/endpoints/posts/reactions/create.ts b/src/server/api/endpoints/posts/reactions/create.ts
index f1b0c7dd29..71fa6a2955 100644
--- a/src/server/api/endpoints/posts/reactions/create.ts
+++ b/src/server/api/endpoints/posts/reactions/create.ts
@@ -3,20 +3,11 @@
  */
 import $ from 'cafy';
 import Reaction from '../../../../../models/post-reaction';
-import Post, { pack as packPost } from '../../../../../models/post';
-import { pack as packUser } from '../../../../../models/user';
-import Watching from '../../../../../models/post-watching';
-import watch from '../../../../../post/watch';
-import { publishPostStream } from '../../../../../publishers/stream';
-import notify from '../../../../../publishers/notify';
-import pushSw from '../../../../../publishers/push-sw';
+import Post from '../../../../../models/post';
+import create from '../../../../../services/post/reaction/create';
 
 /**
  * React to a post
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'postId' parameter
@@ -46,78 +37,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 		return rej('post not found');
 	}
 
-	// Myself
-	if (post.userId.equals(user._id)) {
-		return rej('cannot react to my post');
+	try {
+		await create(user, post, reaction);
+	} catch (e) {
+		rej(e);
 	}
 
-	// if already reacted
-	const exist = await Reaction.findOne({
-		postId: post._id,
-		userId: user._id,
-		deletedAt: { $exists: false }
-	});
-
-	if (exist !== null) {
-		return rej('already reacted');
-	}
-
-	// Create reaction
-	await Reaction.insert({
-		createdAt: new Date(),
-		postId: post._id,
-		userId: user._id,
-		reaction: reaction
-	});
-
-	// Send response
 	res();
-
-	const inc = {};
-	inc[`reactionCounts.${reaction}`] = 1;
-
-	// Increment reactions count
-	await Post.update({ _id: post._id }, {
-		$inc: inc
-	});
-
-	publishPostStream(post._id, 'reacted');
-
-	// Notify
-	notify(post.userId, user._id, 'reaction', {
-		postId: post._id,
-		reaction: reaction
-	});
-
-	pushSw(post.userId, 'reaction', {
-		user: await packUser(user, post.userId),
-		post: await packPost(post, post.userId),
-		reaction: reaction
-	});
-
-	// Fetch watchers
-	Watching
-		.find({
-			postId: post._id,
-			userId: { $ne: user._id },
-			// 削除されたドキュメントは除く
-			deletedAt: { $exists: false }
-		}, {
-			fields: {
-				userId: true
-			}
-		})
-		.then(watchers => {
-			watchers.forEach(watcher => {
-				notify(watcher.userId, user._id, 'reaction', {
-					postId: post._id,
-					reaction: reaction
-				});
-			});
-		});
-
-	// この投稿をWatchする
-	if (user.account.settings.autoWatch !== false) {
-		watch(user._id, post);
-	}
 });
diff --git a/src/services/post/reaction/create.ts b/src/services/post/reaction/create.ts
index e69de29bb2..c26efcfc75 100644
--- a/src/services/post/reaction/create.ts
+++ b/src/services/post/reaction/create.ts
@@ -0,0 +1,94 @@
+import { IUser, pack as packUser, isLocalUser, isRemoteUser } from '../../../models/user';
+import Post, { IPost, pack as packPost } from '../../../models/post';
+import PostReaction from '../../../models/post-reaction';
+import { publishPostStream } from '../../../publishers/stream';
+import notify from '../../../publishers/notify';
+import pushSw from '../../../publishers/push-sw';
+import PostWatching from '../../../models/post-watching';
+import watch from '../watch';
+import renderLike from '../../../remote/activitypub/renderer/like';
+import { deliver } from '../../../queue';
+import context from '../../../remote/activitypub/renderer/context';
+
+export default async (user: IUser, post: IPost, reaction: string) => new Promise(async (res, rej) => {
+	// Myself
+	if (post.userId.equals(user._id)) {
+		return rej('cannot react to my post');
+	}
+
+	// if already reacted
+	const exist = await PostReaction.findOne({
+		postId: post._id,
+		userId: user._id
+	});
+
+	if (exist !== null) {
+		return rej('already reacted');
+	}
+
+	// Create reaction
+	await PostReaction.insert({
+		createdAt: new Date(),
+		postId: post._id,
+		userId: user._id,
+		reaction
+	});
+
+	res();
+
+	const inc = {};
+	inc[`reactionCounts.${reaction}`] = 1;
+
+	// Increment reactions count
+	await Post.update({ _id: post._id }, {
+		$inc: inc
+	});
+
+	publishPostStream(post._id, 'reacted');
+
+	// Notify
+	notify(post.userId, user._id, 'reaction', {
+		postId: post._id,
+		reaction: reaction
+	});
+
+	pushSw(post.userId, 'reaction', {
+		user: await packUser(user, post.userId),
+		post: await packPost(post, post.userId),
+		reaction: reaction
+	});
+
+	// Fetch watchers
+	PostWatching
+		.find({
+			postId: post._id,
+			userId: { $ne: user._id }
+		}, {
+			fields: {
+				userId: true
+			}
+		})
+		.then(watchers => {
+			watchers.forEach(watcher => {
+				notify(watcher.userId, user._id, 'reaction', {
+					postId: post._id,
+					reaction: reaction
+				});
+			});
+		});
+
+	// ユーザーがローカルユーザーかつ自動ウォッチ設定がオンならばこの投稿をWatchする
+	if (isLocalUser(user) && user.account.settings.autoWatch !== false) {
+		watch(user._id, post);
+	}
+
+	//#region 配信
+	const content = renderLike(user, post);
+	content['@context'] = context;
+
+	// リアクターがローカルユーザーかつリアクション対象がリモートユーザーの投稿なら配送
+	if (isLocalUser(user) && isRemoteUser(post._user)) {
+		deliver(user, content, post._user.account.inbox).save();
+	}
+	//#endregion
+});