diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index 58e764ee17..f889374139 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -17,7 +17,7 @@ import packFeed from './feed';
 import { fetchMeta } from '../../misc/fetch-meta';
 import { genOpenapiSpec } from '../api/openapi/gen-spec';
 import config from '../../config';
-import { Users, Notes, Emojis, UserProfiles, Pages, Channels } from '../../models';
+import { Users, Notes, Emojis, UserProfiles, Pages, Channels, Clips } from '../../models';
 import parseAcct from '../../misc/acct/parse';
 import getNoteSummary from '../../misc/get-note-summary';
 import { ensure } from '../../prelude/ensure';
@@ -298,6 +298,28 @@ router.get('/@:user/pages/:page', async ctx => {
 	ctx.status = 404;
 });
 
+// Clip
+router.get('/clips/:clip', async ctx => {
+	const clip = await Clips.findOne({
+		id: ctx.params.clip,
+	});
+
+	if (clip) {
+		const _clip = await Clips.pack(clip);
+		const meta = await fetchMeta();
+		await ctx.render('clip', {
+			clip: _clip,
+			instanceName: meta.name || 'Misskey'
+		});
+
+		ctx.set('Cache-Control', 'public, max-age=180');
+
+		return;
+	}
+
+	ctx.status = 404;
+});
+
 // Channel
 router.get('/channels/:channel', async ctx => {
 	const channel = await Channels.findOne({
diff --git a/src/server/web/views/clip.pug b/src/server/web/views/clip.pug
new file mode 100644
index 0000000000..8cd1c673ed
--- /dev/null
+++ b/src/server/web/views/clip.pug
@@ -0,0 +1,30 @@
+extends ./base
+
+block vars
+	- const user = clip.user;
+	- const title = clip.name;
+	- const url = `${config.url}/clips/${clip.id}`;
+
+block title
+	= `${title} | ${instanceName}`
+
+block desc
+	meta(name='description' content= clip.description)
+
+block og
+	meta(property='og:type'        content='article')
+	meta(property='og:title'       content= title)
+	meta(property='og:description' content= clip.description)
+	meta(property='og:url'         content= url)
+	meta(property='og:image'       content= user.avatarUrl)
+
+block meta
+	meta(name='misskey:user-username' content=user.username)
+	meta(name='misskey:user-id' content=user.id)
+	meta(name='misskey:clip-id' content=clip.id)
+
+	meta(name='twitter:card' content='summary')
+
+	// todo
+	if user.twitter
+		meta(name='twitter:creator' content=`@${user.twitter.screenName}`)