diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ab540c6a4d..a3f2a82ed3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -316,6 +316,8 @@ bannerUrl: "バナー画像のURL"
 basicInfo: "基本情報"
 pinnedUsers: "ピン留めユーザー"
 pinnedUsersDescription: "「みつける」ページなどにピン留めしたいユーザーを改行で区切って記述します。"
+pinnedPages: "ピン留めページ"
+pinnedPagesDescription: "インスタンスのトップページにピン留めしたいページのパスを改行で区切って記述します。"
 hcaptcha: "hCaptcha"
 enableHcaptcha: "hCaptchaを有効にする"
 hcaptchaSiteKey: "サイトキー"
@@ -1117,6 +1119,7 @@ _pages:
   unlike: "いいね解除"
   my: "自分のページ"
   liked: "いいねしたページ"
+  featured: "人気"
   inspector: "インスペクター"
   contents: "コンテンツ"
   content: "ページブロック"
diff --git a/migration/1605585339718-instance-pinned-pages.ts b/migration/1605585339718-instance-pinned-pages.ts
new file mode 100644
index 0000000000..2f0ebab235
--- /dev/null
+++ b/migration/1605585339718-instance-pinned-pages.ts
@@ -0,0 +1,14 @@
+import {MigrationInterface, QueryRunner} from "typeorm";
+
+export class instancePinnedPages1605585339718 implements MigrationInterface {
+    name = 'instancePinnedPages1605585339718'
+
+    public async up(queryRunner: QueryRunner): Promise<void> {
+        await queryRunner.query(`ALTER TABLE "meta" ADD "pinnedPages" character varying(512) array NOT NULL DEFAULT '{"/announcements", "/featured", "/channels", "/pages", "/explore", "/games/reversi", "/about-misskey"}'::varchar[]`);
+    }
+
+    public async down(queryRunner: QueryRunner): Promise<void> {
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedPages"`);
+    }
+
+}
diff --git a/src/client/components/notes.vue b/src/client/components/notes.vue
index f2ea7e929b..649e7c4cf6 100644
--- a/src/client/components/notes.vue
+++ b/src/client/components/notes.vue
@@ -8,7 +8,7 @@
 	<MkError v-if="error" @retry="init()"/>
 
 	<div v-show="more && reversed" style="margin-bottom: var(--margin);">
-		<button class="_loadMore" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
+		<button class="_loadMore" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
 			<template v-if="moreFetching"><MkLoading inline/></template>
 		</button>
diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue
index 7278c74d9a..aca4d32a22 100644
--- a/src/client/components/tab.vue
+++ b/src/client/components/tab.vue
@@ -1,26 +1,32 @@
-<template>
-<div class="pxhvhrfw" v-size="{ max: [500] }">
-	<button v-for="item in items" class="_button" @click="$emit('update:value', item.value)" :class="{ active: value === item.value }" :disabled="value === item.value" :key="item.value"><Fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button>
-</div>
-</template>
-
 <script lang="ts">
-import { defineComponent } from 'vue';
+import { defineComponent, h, resolveDirective, withDirectives } from 'vue';
 
 export default defineComponent({
 	props: {
-		items: {
-			type: Array,
-			required: true,
-		},
 		value: {
 			required: true,
 		},
 	},
+	render() {
+		const options = this.$slots.default();
+
+		return withDirectives(h('div', {
+			class: 'pxhvhrfw',
+		}, options.map(option => h('button', {
+			class: ['_button', { active: this.value === option.props.value }],
+			key: option.props.value,
+			disabled: this.value === option.props.value,
+			onClick: () => {
+				this.$emit('update:value', option.props.value);
+			}
+		}, option.children))), [
+			[resolveDirective('size'), { max: [500] }]
+		]);
+	}
 });
 </script>
 
-<style lang="scss" scoped>
+<style lang="scss">
 .pxhvhrfw {
 	display: flex;
 
diff --git a/src/client/components/taskmanager.api-window.vue b/src/client/components/taskmanager.api-window.vue
index 8a6f200a29..0df3f75fa2 100644
--- a/src/client/components/taskmanager.api-window.vue
+++ b/src/client/components/taskmanager.api-window.vue
@@ -9,7 +9,10 @@
 	<template #header>Req Viewer</template>
 
 	<div class="rlkneywz">
-		<MkTab v-model:value="tab" :items="[{ label: 'Request', value: 'req', }, { label: 'Response', value: 'res', }]" style="border-bottom: solid 1px var(--divider);"/>
+		<MkTab v-model:value="tab" style="border-bottom: solid 1px var(--divider);">
+			<option value="req">Request</option>
+			<option value="res">Response</option>
+		</MkTab>
 
 		<code v-if="tab === 'req'">{{ reqStr }}</code>
 		<code v-if="tab === 'res'">{{ resStr }}</code>
diff --git a/src/client/components/taskmanager.vue b/src/client/components/taskmanager.vue
index ab8d4a80dd..92c56442c3 100644
--- a/src/client/components/taskmanager.vue
+++ b/src/client/components/taskmanager.vue
@@ -4,7 +4,12 @@
 		<Fa :icon="faTerminal" style="margin-right: 0.5em;"/>Task Manager
 	</template>
 	<div class="qljqmnzj">
-		<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/>
+		<MkTab v-model:value="tab" style="border-bottom: solid 1px var(--divider);">
+			<option value="windows">Windows</option>
+			<option value="stream">Stream</option>
+			<option value="streamPool">Stream (Pool)</option>
+			<option value="api">API</option>
+		</MkTab>
 
 		<div class="content">
 			<div v-if="tab === 'windows'" class="windows" v-follow>
diff --git a/src/client/pages/channel.vue b/src/client/pages/channel.vue
index 33339bbc95..ef41308541 100644
--- a/src/client/pages/channel.vue
+++ b/src/client/pages/channel.vue
@@ -20,7 +20,7 @@
 		</div>
 	</div>
 
-	<XPostForm :channel="channel" class="post-form _content _panel _vMargin" fixed/>
+	<XPostForm :channel="channel" class="post-form _content _panel _vMargin" fixed v-if="this.$store.getters.isSignedIn"/>
 
 	<XTimeline class="_content _vMargin" src="channel" :channel="channelId" @before="before" @after="after"/>
 </div>
diff --git a/src/client/pages/channels.vue b/src/client/pages/channels.vue
index a57f974c4d..e428051284 100644
--- a/src/client/pages/channels.vue
+++ b/src/client/pages/channels.vue
@@ -1,26 +1,30 @@
 <template>
 <div>
-	<div class="_section" style="padding: 0;">
-		<MkTab class="_content" v-model:value="tab" :items="[{ label: $t('_channel.featured'), value: 'featured', icon: faFireAlt }, { label: $t('_channel.following'), value: 'following', icon: faHeart }, { label: $t('_channel.owned'), value: 'owned', icon: faEdit }]"/>
+	<div class="_section" style="padding: 0;" v-if="this.$store.getters.isSignedIn">
+		<MkTab class="_content" v-model:value="tab">
+			<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_channel.featured') }}</option>
+			<option value="following"><Fa :icon="faHeart"/> {{ $t('_channel.following') }}</option>
+			<option value="owned"><Fa :icon="faEdit"/> {{ $t('_channel.owned') }}</option>
+		</MkTab>
 	</div>
 
 	<div class="_section">
 		<div class="_content grwlizim featured" v-if="tab === 'featured'">
 			<MkPagination :pagination="featuredPagination" #default="{items}">
-				<MkChannelPreview v-for="channel in items" class="uveselbe" :channel="channel" :key="channel.id"/>
+				<MkChannelPreview v-for="channel in items" class="_vMargin" :channel="channel" :key="channel.id"/>
 			</MkPagination>
 		</div>
 
 		<div class="_content grwlizim following" v-if="tab === 'following'">
 			<MkPagination :pagination="followingPagination" #default="{items}">
-				<MkChannelPreview v-for="channel in items" class="uveselbe" :channel="channel" :key="channel.id"/>
+				<MkChannelPreview v-for="channel in items" class="_vMargin" :channel="channel" :key="channel.id"/>
 			</MkPagination>
 		</div>
 
 		<div class="_content grwlizim owned" v-if="tab === 'owned'">
 			<MkButton class="new" @click="create()"><Fa :icon="faPlus"/></MkButton>
 			<MkPagination :pagination="ownedPagination" #default="{items}">
-				<MkChannelPreview v-for="channel in items" class="uveselbe" :channel="channel" :key="channel.id"/>
+				<MkChannelPreview v-for="channel in items" class="_vMargin" :channel="channel" :key="channel.id"/>
 			</MkPagination>
 		</div>
 	</div>
@@ -44,7 +48,11 @@ export default defineComponent({
 		return {
 			INFO: {
 				title: this.$t('channel'),
-				icon: faSatelliteDish
+				icon: faSatelliteDish,
+				action: {
+					icon: faPlus,
+					handler: this.create
+				}
 			},
 			tab: 'featured',
 			featuredPagination: {
@@ -69,23 +77,3 @@ export default defineComponent({
 	}
 });
 </script>
-
-<style lang="scss" scoped>
-.grwlizim {
-	padding: 16px 0;
-
-	&.my .uveselbe:first-child {
-		margin-top: 16px;
-	}
-
-	.uveselbe:not(:last-child) {
-		margin-bottom: 8px;
-	}
-
-	@media (min-width: 500px) {
-		.uveselbe:not(:last-child) {
-			margin-bottom: 16px;
-		}
-	}
-}
-</style>
diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue
index dcd12edc91..01ea0d7f82 100644
--- a/src/client/pages/instance/emojis.vue
+++ b/src/client/pages/instance/emojis.vue
@@ -1,7 +1,10 @@
 <template>
 <div class="mk-instance-emojis">
 	<div class="_section" style="padding: 0;">
-		<MkTab v-model:value="tab" :items="[{ label: $t('local'), value: 'local' }, { label: $t('remote'), value: 'remote' }]"/>
+		<MkTab v-model:value="tab">
+			<option value="local">{{ $t('local') }}</option>
+			<option value="remote">{{ $t('remote') }}</option>
+		</MkTab>
 	</div>
 
 	<div class="_section">
diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue
index 1015b5f98d..32a6a9595f 100644
--- a/src/client/pages/instance/settings.vue
+++ b/src/client/pages/instance/settings.vue
@@ -1,6 +1,6 @@
 <template>
-<div v-if="meta">
-	<section class="_section info">
+<div v-if="meta" class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faInfoCircle"/> {{ $t('basicInfo') }}</div>
 		<div class="_content">
 			<MkInput v-model:value="name">{{ $t('instanceName') }}</MkInput>
@@ -16,7 +16,7 @@
 		</div>
 	</section>
 
-	<section class="_section info">
+	<section class="_card _vMargin">
 		<div class="_content">
 			<MkInput v-model:value="maxNoteTextLength" type="number" :save="() => save()"><template #icon><Fa :icon="faPencilAlt"/></template>{{ $t('maxNoteTextLength') }}</MkInput>
 		</div>
@@ -30,7 +30,7 @@
 		</div>
 	</section>
 
-	<section class="_section info">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faUser"/> {{ $t('registration') }}</div>
 		<div class="_content">
 			<MkSwitch v-model:value="enableRegistration" @update:value="save()">{{ $t('enableRegistration') }}</MkSwitch>
@@ -38,7 +38,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faShieldAlt"/> {{ $t('hcaptcha') }}</div>
 		<div class="_content">
 			<MkSwitch v-model:value="enableHcaptcha">{{ $t('enableHcaptcha') }}</MkSwitch>
@@ -56,7 +56,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faShieldAlt"/> {{ $t('recaptcha') }}</div>
 		<div class="_content">
 			<MkSwitch v-model:value="enableRecaptcha" ref="enableRecaptcha">{{ $t('enableRecaptcha') }}</MkSwitch>
@@ -74,7 +74,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faEnvelope" /> {{ $t('emailConfig') }}</div>
 		<div class="_content">
 			<MkSwitch v-model:value="enableEmail" @update:value="save()">{{ $t('enableEmail') }}<template #desc>{{ $t('emailConfigInfo') }}</template></MkSwitch>
@@ -97,7 +97,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faBolt"/> {{ $t('serviceworker') }}</div>
 		<div class="_content">
 			<MkSwitch v-model:value="enableServiceWorker">{{ $t('enableServiceworker') }}<template #desc>{{ $t('serviceworkerInfo') }}</template></MkSwitch>
@@ -113,7 +113,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faThumbtack"/> {{ $t('pinnedUsers') }}</div>
 		<div class="_content">
 			<MkTextarea v-model:value="pinnedUsers">
@@ -125,7 +125,19 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
+		<div class="_title"><Fa :icon="faThumbtack"/> {{ $t('pinnedPages') }}</div>
+		<div class="_content">
+			<MkTextarea v-model:value="pinnedPages">
+				<template #desc>{{ $t('pinnedPagesDescription') }}</template>
+			</MkTextarea>
+		</div>
+		<div class="_footer">
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+		</div>
+	</section>
+
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faCloud"/> {{ $t('files') }}</div>
 		<div class="_content">
 			<MkSwitch v-model:value="cacheRemoteFiles">{{ $t('cacheRemoteFiles') }}<template #desc>{{ $t('cacheRemoteFilesDescription') }}</template></MkSwitch>
@@ -138,7 +150,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faCloud"/> {{ $t('objectStorage') }}</div>
 		<div class="_content">
 			<MkSwitch v-model:value="useObjectStorage">{{ $t('useObjectStorage') }}</MkSwitch>
@@ -166,7 +178,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faGhost"/> {{ $t('proxyAccount') }}</div>
 		<div class="_content">
 			<MkInput :value="proxyAccount ? proxyAccount.username : null" disabled><template #prefix>@</template>{{ $t('proxyAccount') }}<template #desc>{{ $t('proxyAccountDescription') }}</template></MkInput>
@@ -174,7 +186,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faBan"/> {{ $t('blockedInstances') }}</div>
 		<div class="_content">
 			<MkTextarea v-model:value="blockedHosts">
@@ -186,7 +198,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faShareAlt"/> {{ $t('integration') }}</div>
 		<div class="_content">
 			<header><Fa :icon="faTwitter"/> Twitter</header>
@@ -220,7 +232,7 @@
 		</div>
 	</section>
 
-	<section class="_section">
+	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faArchway" /> Summaly Proxy</div>
 		<div class="_content">
 			<MkInput v-model:value="summalyProxy">URL</MkInput>
@@ -260,6 +272,7 @@ export default defineComponent({
 				title: this.$t('instance'),
 				icon: faCog,
 			},
+			meta: null,
 			url,
 			proxyAccount: null,
 			proxyAccountId: null,
@@ -269,6 +282,7 @@ export default defineComponent({
 			remoteDriveCapacityMb: 0,
 			blockedHosts: '',
 			pinnedUsers: '',
+			pinnedPages: '',
 			maintainerName: null,
 			maintainerEmail: null,
 			name: null,
@@ -323,13 +337,9 @@ export default defineComponent({
 		}
 	},
 
-	computed: {
-		meta() {
-			return this.$store.state.instance.meta;
-		},
-	},
+	async created() {
+		this.meta = await os.api('meta', { detail: true });
 
-	created() {
 		this.name = this.meta.name;
 		this.description = this.meta.description;
 		this.tosUrl = this.meta.tosUrl;
@@ -356,6 +366,7 @@ export default defineComponent({
 		this.remoteDriveCapacityMb = this.meta.driveCapacityPerRemoteUserMb;
 		this.blockedHosts = this.meta.blockedHosts.join('\n');
 		this.pinnedUsers = this.meta.pinnedUsers.join('\n');
+		this.pinnedPages = this.meta.pinnedPages.join('\n');
 		this.enableServiceWorker = this.meta.enableServiceWorker;
 		this.swPublicKey = this.meta.swPublickey;
 		this.swPrivateKey = this.meta.swPrivateKey;
@@ -506,6 +517,7 @@ export default defineComponent({
 				remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
 				blockedHosts: this.blockedHosts.split('\n') || [],
 				pinnedUsers: this.pinnedUsers ? this.pinnedUsers.split('\n') : [],
+				pinnedPages: this.pinnedPages ? this.pinnedPages.split('\n') : [],
 				enableServiceWorker: this.enableServiceWorker,
 				swPublicKey: this.swPublicKey,
 				swPrivateKey: this.swPrivateKey,
diff --git a/src/client/pages/my-groups/index.vue b/src/client/pages/my-groups/index.vue
index d81165b2dc..fb3d9ccb34 100644
--- a/src/client/pages/my-groups/index.vue
+++ b/src/client/pages/my-groups/index.vue
@@ -1,7 +1,11 @@
 <template>
 <div class="">
 	<div class="_section" style="padding: 0;">
-		<MkTab v-model:value="tab" :items="[{ label: $t('ownedGroups'), value: 'owned' }, { label: $t('joinedGroups'), value: 'joined' }, { label: $t('invites'), icon: faEnvelopeOpenText, value: 'invites' }]"/>
+		<MkTab v-model:value="tab">
+			<option value="owned">{{ $t('ownedGroups') }}</option>
+			<option value="joined">{{ $t('joinedGroups') }}</option>
+			<option value="invites"><Fa :icon="faEnvelopeOpenText"/> {{ $t('invites') }}</option>
+		</MkTab>
 	</div>
 
 	<div class="_section">
diff --git a/src/client/pages/note.vue b/src/client/pages/note.vue
index 7f416c7558..6ad6f2ba1b 100644
--- a/src/client/pages/note.vue
+++ b/src/client/pages/note.vue
@@ -1,21 +1,31 @@
 <template>
 <div class="fcuexfpr">
 	<div v-if="note" class="note">
-		<div class="_section">
-			<XNotes v-if="showNext" class="_content" :pagination="next"/>
-			<MkButton v-else-if="hasNext" class="load _content" @click="showNext = true"><Fa :icon="faChevronUp"/></MkButton>
+		<div class="_section" v-if="showNext">
+			<XNotes class="_content" :pagination="next"/>
 		</div>
 
-		<div class="_section">
-			<div class="_content">
+		<div class="_section main">
+			<MkButton v-if="!showNext && hasNext" class="load next _content" @click="showNext = true"><Fa :icon="faChevronUp"/></MkButton>
+			<div class="_content _vMargin">
 				<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_vMargin"/>
 				<XNote v-model:note="note" :key="note.id" :detail="true" class="_vMargin"/>
 			</div>
+			<div class="_content clips _vMargin" v-if="clips && clips.length > 0">
+				<div class="title">{{ $t('clip') }}</div>
+				<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _vMargin">
+					<b>{{ item.name }}</b>
+					<div v-if="item.description" class="description">{{ item.description }}</div>
+					<div class="user">
+						<MkAvatar :user="item.user" class="avatar"/> <MkUserName :user="item.user" :nowrap="false"/>
+					</div>
+				</MkA>
+			</div>
+			<MkButton v-if="!showPrev && hasPrev" class="load prev _content" @click="showPrev = true"><Fa :icon="faChevronDown"/></MkButton>
 		</div>
 
-		<div class="_section">
-			<XNotes v-if="showPrev" class="_content" :pagination="prev"/>
-			<MkButton v-else-if="hasPrev" class="load _content" @click="showPrev = true"><Fa :icon="faChevronDown"/></MkButton>
+		<div class="_section" v-if="showPrev">
+			<XNotes class="_content" :pagination="prev"/>
 		</div>
 	</div>
 
@@ -28,7 +38,6 @@
 <script lang="ts">
 import { computed, defineComponent } from 'vue';
 import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
-import Progress from '@/scripts/loading';
 import XNote from '@/components/note.vue';
 import XNotes from '@/components/notes.vue';
 import MkRemoteCaution from '@/components/remote-caution.vue';
@@ -55,6 +64,7 @@ export default defineComponent({
 				avatar: this.note.user,
 			} : null),
 			note: null,
+			clips: null,
 			hasPrev: false,
 			hasNext: false,
 			showPrev: false,
@@ -88,11 +98,13 @@ export default defineComponent({
 	},
 	methods: {
 		fetch() {
-			Progress.start();
 			os.api('notes/show', {
 				noteId: this.noteId
 			}).then(note => {
 				Promise.all([
+					os.api('notes/clips', {
+						noteId: note.id,
+					}),
 					os.api('users/notes', {
 						userId: note.userId,
 						untilId: note.id,
@@ -103,15 +115,14 @@ export default defineComponent({
 						sinceId: note.id,
 						limit: 1,
 					}),
-				]).then(([prev, next]) => {
+				]).then(([clips, prev, next]) => {
+					this.clips = clips;
 					this.hasPrev = prev.length !== 0;
 					this.hasNext = next.length !== 0;
 					this.note = note;
 				});
 			}).catch(e => {
 				this.error = e;
-			}).finally(() => {
-				Progress.done();
 			});
 		}
 	}
@@ -121,10 +132,46 @@ export default defineComponent({
 <style lang="scss" scoped>
 .fcuexfpr {
 	> .note {
-		> ._section {
+		> .main {
 			> .load {
 				min-width: 0;
 				border-radius: 999px;
+
+				&.next {
+					margin-bottom: var(--margin);
+				}
+
+				&.prev {
+					margin-top: var(--margin);
+				}
+			}
+
+			> .clips {
+				> .title {
+					font-weight: bold;
+					padding: 12px;
+				}
+
+				> .item {
+					display: block;
+					padding: 16px;
+
+					> .description {
+						padding: 8px 0;
+					}
+
+					> .user {
+						$height: 32px;
+						padding-top: 16px;
+						border-top: solid 1px var(--divider);
+						line-height: $height;
+
+						> .avatar {
+							width: $height;
+							height: $height;
+						}
+					}
+				}
 			}
 		}
 	}
diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue
index 30146b56ed..eab48c766e 100644
--- a/src/client/pages/page-editor/page-editor.vue
+++ b/src/client/pages/page-editor/page-editor.vue
@@ -277,7 +277,7 @@ export default defineComponent({
 						type: 'success',
 						text: this.$t('_pages.created')
 					});
-					this.$router.push(`/my/pages/edit/${this.pageId}`);
+					this.$router.push(`/pages/edit/${this.pageId}`);
 				}).catch(onError);
 			}
 		},
@@ -296,7 +296,7 @@ export default defineComponent({
 						type: 'success',
 						text: this.$t('_pages.deleted')
 					});
-					this.$router.push(`/my/pages`);
+					this.$router.push(`/pages`);
 				});
 			});
 		},
diff --git a/src/client/pages/page.vue b/src/client/pages/page.vue
index d1df8f796f..43c1688824 100644
--- a/src/client/pages/page.vue
+++ b/src/client/pages/page.vue
@@ -22,7 +22,7 @@
 		<div class="_content">
 			<MkA :to="`./${page.name}/view-source`" class="link">{{ $t('_pages.viewSource') }}</MkA>
 			<template v-if="$store.getters.isSignedIn && $store.state.i.id === page.userId">
-				<MkA :to="`/my/pages/edit/${page.id}`" class="link">{{ $t('_pages.editThisPage') }}</MkA>
+				<MkA :to="`/pages/edit/${page.id}`" class="link">{{ $t('_pages.editThisPage') }}</MkA>
 				<button v-if="$store.state.i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $t('unpin') }}</button>
 				<button v-else @click="pin(true)" class="link _textButton">{{ $t('pin') }}</button>
 			</template>
diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue
index 4e0ff5dd3c..140bbcb873 100644
--- a/src/client/pages/pages.vue
+++ b/src/client/pages/pages.vue
@@ -1,8 +1,18 @@
 <template>
 <div>
-	<MkTab v-model:value="tab" :items="[{ label: $t('_pages.my'), value: 'my', icon: faEdit }, { label: $t('_pages.liked'), value: 'liked', icon: faHeart }]"/>
+	<MkTab v-model:value="tab" v-if="this.$store.getters.isSignedIn">
+		<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_pages.featured') }}</option>
+		<option value="my"><Fa :icon="faEdit"/> {{ $t('_pages.my') }}</option>
+		<option value="liked"><Fa :icon="faHeart"/> {{ $t('_pages.liked') }}</option>
+	</MkTab>
 
 	<div class="_section">
+		<div class="rknalgpo _content" v-if="tab === 'featured'">
+			<MkPagination :pagination="featuredPagesPagination" #default="{items}">
+				<MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/>
+			</MkPagination>
+		</div>
+
 		<div class="rknalgpo _content my" v-if="tab === 'my'">
 			<MkButton class="new" @click="create()"><Fa :icon="faPlus"/></MkButton>
 			<MkPagination :pagination="myPagesPagination" #default="{items}">
@@ -21,7 +31,7 @@
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import { faPlus, faEdit } from '@fortawesome/free-solid-svg-icons';
+import { faPlus, faEdit, faFireAlt } from '@fortawesome/free-solid-svg-icons';
 import { faStickyNote, faHeart } from '@fortawesome/free-regular-svg-icons';
 import MkPagePreview from '@/components/page-preview.vue';
 import MkPagination from '@/components/ui/pagination.vue';
@@ -42,7 +52,11 @@ export default defineComponent({
 					handler: this.create
 				}
 			},
-			tab: 'my',
+			tab: 'featured',
+			featuredPagesPagination: {
+				endpoint: 'pages/featured',
+				noPaging: true,
+			},
 			myPagesPagination: {
 				endpoint: 'i/pages',
 				limit: 5,
@@ -51,12 +65,12 @@ export default defineComponent({
 				endpoint: 'i/page-likes',
 				limit: 5,
 			},
-			faStickyNote, faPlus, faEdit, faHeart
+			faStickyNote, faPlus, faEdit, faHeart, faFireAlt
 		};
 	},
 	methods: {
 		create() {
-			this.$router.push(`/my/pages/new`);
+			this.$router.push(`/pages/new`);
 		}
 	}
 });
diff --git a/src/client/pages/settings/mute-block.vue b/src/client/pages/settings/mute-block.vue
index 2143d108b5..43e2c396b9 100644
--- a/src/client/pages/settings/mute-block.vue
+++ b/src/client/pages/settings/mute-block.vue
@@ -1,6 +1,9 @@
 <template>
 <section class="rrfwjxfl _section">
-	<MkTab v-model:value="tab" :items="[{ label: $t('mutedUsers'), value: 'mute' }, { label: $t('blockedUsers'), value: 'block' }]" style="margin-bottom: var(--margin);"/>
+	<MkTab v-model:value="tab" style="margin-bottom: var(--margin);">
+		<option value="mute">{{ $t('mutedUsers') }}</option>
+		<option value="block">{{ $t('blockedUsers') }}</option>
+	</MkTab>
 	<div class="_content" v-if="tab === 'mute'">
 		<MkPagination :pagination="mutingPagination" class="muting">
 			<template #empty><MkInfo>{{ $t('noUsers') }}</MkInfo></template>
diff --git a/src/client/pages/settings/word-mute.vue b/src/client/pages/settings/word-mute.vue
index aeae031830..444b2e598c 100644
--- a/src/client/pages/settings/word-mute.vue
+++ b/src/client/pages/settings/word-mute.vue
@@ -1,7 +1,10 @@
 <template>
 <div class="_section">
 	<div class="_card">
-		<MkTab v-model:value="tab" :items="[{ label: $t('_wordMute.soft'), value: 'soft' }, { label: $t('_wordMute.hard'), value: 'hard' }]"/>
+		<MkTab v-model:value="tab">
+			<option value="soft">{{ $t('_wordMute.soft') }}</option>
+			<option value="hard">{{ $t('_wordMute.hard') }}</option>
+		</MkTab>
 		<div class="_content">
 			<div v-show="tab === 'soft'">
 				<MkInfo>{{ $t('_wordMute.softDescription') }}</MkInfo>
diff --git a/src/client/pages/welcome.entrance.block.vue b/src/client/pages/welcome.entrance.block.vue
new file mode 100644
index 0000000000..0e4aefa4b0
--- /dev/null
+++ b/src/client/pages/welcome.entrance.block.vue
@@ -0,0 +1,141 @@
+<template>
+<div class="xyeqzsjl _panel">
+	<header>
+		<button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
+		<XHeader class="title" :info="pageInfo" :with-back="false"/>
+	</header>
+	<div>
+		<component :is="component" v-bind="props" :ref="changePage"/>
+	</div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
+import XWindow from '@/components/ui/window.vue';
+import XHeader from '@/ui/_common_/header.vue';
+import { popout } from '@/scripts/popout';
+import { resolve } from '@/router';
+import { url } from '@/config';
+
+export default defineComponent({
+	components: {
+		XWindow,
+		XHeader,
+	},
+
+	provide() {
+		return {
+			navHook: (path) => {
+				this.navigate(path);
+			}
+		};
+	},
+
+	props: {
+		initialPath: {
+			type: String,
+			required: true,
+		},
+	},
+
+	data() {
+		return {
+			pageInfo: null,
+			path: this.initialPath,
+			component: null,
+			props: null,
+			history: [],
+			faChevronLeft,
+		};
+	},
+
+	computed: {
+		url(): string {
+			return url + this.path;
+		},
+	},
+
+	created() {
+		const { component, props } = resolve(this.initialPath);
+		this.component = component;
+		this.props = props;
+	},
+
+	methods: {
+		changePage(page) {
+			if (page == null) return;
+			if (page.INFO) {
+				this.pageInfo = page.INFO;
+			}
+		},
+
+		navigate(path, record = true) {
+			if (record) this.history.push(this.path);
+			this.path = path;
+			const { component, props } = resolve(path);
+			this.component = component;
+			this.props = props;
+		},
+
+		back() {
+			this.navigate(this.history.pop(), false);
+		},
+
+		expand() {
+			this.$router.push(this.path);
+			this.$refs.window.close();
+		},
+
+		popout() {
+			popout(this.path, this.$el);
+			this.$refs.window.close();
+		},
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.xyeqzsjl {
+	--section-padding: 16px;
+
+	display: flex;
+	flex-direction: column;
+	contain: content;
+
+	> header {
+		$height: 50px;
+		display: flex;
+		position: relative;
+		z-index: 1;
+		height: $height;
+		line-height: $height;
+		box-shadow: 0px 1px var(--divider);
+
+		> button {
+			height: $height;
+			width: $height;
+
+			&:hover {
+				color: var(--fgHighlighted);
+			}
+		}
+
+		> .title {
+			flex: 1;
+			position: relative;
+			line-height: $height;
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			text-align: center;
+		}
+	}
+
+	> div {
+		flex: 1;
+		overflow: auto;
+	}
+}
+</style>
diff --git a/src/client/pages/welcome.entrance.vue b/src/client/pages/welcome.entrance.vue
index ff946f7452..b1cd6d50c6 100644
--- a/src/client/pages/welcome.entrance.vue
+++ b/src/client/pages/welcome.entrance.vue
@@ -1,18 +1,13 @@
 <template>
-<div class="rsqzvsbo">
-	<div class="_section">
-		<div class="_content _panel about" v-if="meta">
-			<div class="body">
-				<div class="desc" v-html="meta.description || $t('introMisskey')"></div>
-				<MkButton @click="signup()" style="display: inline-block; margin-right: 16px;" primary>{{ $t('signup') }}</MkButton>
-				<MkButton @click="signin()" style="display: inline-block;">{{ $t('login') }}</MkButton>
-			</div>
-		</div>
+<div class="rsqzvsbo _section" v-if="meta">
+	<div class="about">
+		<h1>{{ instanceName }}</h1>
+		<div class="desc" v-html="meta.description || $t('introMisskey')"></div>
+		<MkButton @click="signup()" style="display: inline-block; margin-right: 16px;" primary>{{ $t('signup') }}</MkButton>
+		<MkButton @click="signin()" style="display: inline-block;">{{ $t('login') }}</MkButton>
 	</div>
-	<div class="_section">
-		<div class="_content">
-			<XNotes :pagination="featuredPagination"/>
-		</div>
+	<div class="blocks">
+		<XBlock class="block" v-for="path in meta.pinnedPages" :initial-path="path" :key="path"/>
 	</div>
 </div>
 </template>
@@ -24,33 +19,30 @@ import XSigninDialog from '@/components/signin-dialog.vue';
 import XSignupDialog from '@/components/signup-dialog.vue';
 import MkButton from '@/components/ui/button.vue';
 import XNotes from '@/components/notes.vue';
-import { host } from '@/config';
+import XBlock from './welcome.entrance.block.vue';
+import { host, instanceName } from '@/config';
 import * as os from '@/os';
 
 export default defineComponent({
 	components: {
 		MkButton,
 		XNotes,
+		XBlock,
 	},
 
 	data() {
 		return {
-			featuredPagination: {
-				endpoint: 'notes/featured',
-				limit: 10,
-				noPaging: true,
-			},
 			host: toUnicode(host),
+			instanceName,
+			meta: null,
 		};
 	},
 
-	computed: {
-		meta() {
-			return this.$store.state.instance.meta;
-		},
-	},
-
 	created() {
+		os.api('meta', { detail: true }).then(meta => {
+			this.meta = meta;
+		});
+
 		os.api('stats').then(stats => {
 			this.stats = stats;
 		});
@@ -74,15 +66,42 @@ export default defineComponent({
 
 <style lang="scss" scoped>
 .rsqzvsbo {
-	> ._section {
-		> .about {
-			> .body {
-				padding: 32px;
+	text-align: center;
 
-				@media (max-width: 500px) {
-					padding: 16px;
-				}
-			}
+	> .about {
+		display: inline-block;
+		padding: 24px;
+		margin-bottom: var(--margin);
+		-webkit-backdrop-filter: blur(8px);
+		backdrop-filter: blur(8px);
+		background: rgba(0, 0, 0, 0.5);
+		border-radius: var(--radius);
+		text-align: center;
+		box-sizing: border-box;
+		min-width: 300px;
+		max-width: 800px;
+
+		&, * {
+			color: #fff !important;
+		}
+
+		> h1 {
+			margin: 0 0 16px 0;
+		}
+	}
+
+	> .blocks {
+		display: grid;
+		grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
+		grid-gap: var(--margin);
+		text-align: left;
+
+		> .block {
+			height: 600px;
+		}
+
+		@media (max-width: 800px) {
+			grid-template-columns: 1fr;
 		}
 	}
 }
diff --git a/src/client/pages/welcome.vue b/src/client/pages/welcome.vue
index 32ac43eb9d..cc57629c8a 100644
--- a/src/client/pages/welcome.vue
+++ b/src/client/pages/welcome.vue
@@ -10,6 +10,7 @@ import { defineComponent } from 'vue';
 import XSetup from './welcome.setup.vue';
 import XEntrance from './welcome.entrance.vue';
 import { instanceName } from '@/config';
+import * as os from '@/os';
 
 export default defineComponent({
 	components: {
@@ -20,16 +21,17 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: instanceName || 'Misskey',
+				title: instanceName,
 				icon: null
 			},
+			meta: null
 		}
 	},
 
-	computed: {
-		meta() {
-			return this.$store.state.instance.meta;
-		},
-	},
+	created() {
+		os.api('meta', { detail: true }).then(meta => {
+			this.meta = meta;
+		});
+	}
 });
 </script>
diff --git a/src/client/router.ts b/src/client/router.ts
index 413e72c320..5ad3345d55 100644
--- a/src/client/router.ts
+++ b/src/client/router.ts
@@ -33,6 +33,9 @@ export const router = createRouter({
 		{ path: '/explore', component: page('explore') },
 		{ path: '/explore/tags/:tag', props: true, component: page('explore') },
 		{ path: '/search', component: page('search') },
+		{ path: '/pages', name: 'pages', component: page('pages') },
+		{ path: '/pages/new', component: page('page-editor/page-editor') },
+		{ path: '/pages/edit/:pageId', component: page('page-editor/page-editor'), props: route => ({ initPageId: route.params.pageId }) },
 		{ path: '/channels', component: page('channels') },
 		{ path: '/channels/new', component: page('channel-editor') },
 		{ path: '/channels/:channelId/edit', component: page('channel-editor'), props: true },
@@ -47,9 +50,6 @@ export const router = createRouter({
 		{ path: '/my/messaging/group/:group', component: page('messaging/messaging-room'), props: route => ({ groupId: route.params.group }) },
 		{ path: '/my/drive', name: 'drive', component: page('drive') },
 		{ path: '/my/drive/folder/:folder', component: page('drive') },
-		{ path: '/my/pages', name: 'pages', component: page('pages') },
-		{ path: '/my/pages/new', component: page('page-editor/page-editor') },
-		{ path: '/my/pages/edit/:pageId', component: page('page-editor/page-editor'), props: route => ({ initPageId: route.params.pageId }) },
 		{ path: '/my/follow-requests', component: page('follow-requests') },
 		{ path: '/my/lists', component: page('my-lists/index') },
 		{ path: '/my/lists/:list', component: page('my-lists/list') },
diff --git a/src/client/sidebar.ts b/src/client/sidebar.ts
index a541670df1..a3a32d7875 100644
--- a/src/client/sidebar.ts
+++ b/src/client/sidebar.ts
@@ -96,8 +96,7 @@ export const sidebarDef = {
 	pages: {
 		title: 'pages',
 		icon: faFileAlt,
-		show: computed(() => store.getters.isSignedIn),
-		to: '/my/pages',
+		to: '/pages',
 	},
 	clips: {
 		title: 'clip',
diff --git a/src/client/ui/visitor.vue b/src/client/ui/visitor.vue
index 8a3c19b631..56cc270be7 100644
--- a/src/client/ui/visitor.vue
+++ b/src/client/ui/visitor.vue
@@ -7,12 +7,12 @@
 		<MkA class="link" to="/about">{{ $t('aboutX', { x: instanceName }) }}</MkA>
 	</header>
 
-	<div class="banner" :style="{ backgroundImage: `url(${ $store.state.instance.meta.bannerUrl })` }">
-		<h1>{{ instanceName }}</h1>
+	<div class="banner" :class="{ asBg: $route.path === '/' }" :style="{ backgroundImage: `url(${ $store.state.instance.meta.bannerUrl })` }">
+		<h1 v-if="$route.path !== '/'">{{ instanceName }}</h1>
 	</div>
 
 	<div class="contents" ref="contents" :class="{ wallpaper }">
-		<header class="header" ref="header">
+		<header class="header" ref="header" v-show="$route.path !== '/'">
 			<XHeader :info="pageInfo"/>
 		</header>
 		<main ref="main">
@@ -116,11 +116,10 @@ export default defineComponent({
 <style lang="scss" scoped>
 .mk-app {
 	min-height: 100vh;
-	max-width: 1300px;
-	margin: 0 auto;
-	box-shadow: 1px 0 var(--divider), -1px 0 var(--divider);
 
 	> header {
+		position: relative;
+		z-index: 1;
 		background: var(--panel);
 		padding: 0 16px;
 		text-align: center;
@@ -145,6 +144,12 @@ export default defineComponent({
 		background-size: cover;
 		background-position: center;
 
+		&.asBg {
+			position: absolute;
+			left: 0;
+			height: 320px;
+		}
+
 		&:after {
 			content: "";
 			display: block;
@@ -166,6 +171,9 @@ export default defineComponent({
 	}
 
 	> .contents {
+		position: relative;
+		z-index: 1;
+
 		> .header {
 			position: sticky;
 			top: 0;
diff --git a/src/models/entities/meta.ts b/src/models/entities/meta.ts
index d1eecf6277..b7fe8b18ad 100644
--- a/src/models/entities/meta.ts
+++ b/src/models/entities/meta.ts
@@ -76,6 +76,11 @@ export class Meta {
 	})
 	public blockedHosts: string[];
 
+	@Column('varchar', {
+		length: 512, array: true, default: '{"/announcements", "/featured", "/channels", "/explore", "/games/reversi", "/about-misskey"}'
+	})
+	public pinnedPages: string[];
+
 	@Column('varchar', {
 		length: 512,
 		nullable: true,
diff --git a/src/models/repositories/page.ts b/src/models/repositories/page.ts
index 662c41905f..3889bf59a7 100644
--- a/src/models/repositories/page.ts
+++ b/src/models/repositories/page.ts
@@ -85,8 +85,9 @@ export class PageRepository extends Repository<Page> {
 
 	public packMany(
 		pages: Page[],
+		me?: User['id'] | User | null | undefined,
 	) {
-		return Promise.all(pages.map(x => this.pack(x)));
+		return Promise.all(pages.map(x => this.pack(x, me)));
 	}
 }
 
diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts
index fea6cb539f..ae6d2a4163 100644
--- a/src/server/api/endpoints/admin/update-meta.ts
+++ b/src/server/api/endpoints/admin/update-meta.ts
@@ -208,6 +208,10 @@ export const meta = {
 			}
 		},
 
+		pinnedPages: {
+			validator: $.optional.arr($.str),
+		},
+
 		langs: {
 			validator: $.optional.arr($.str),
 			desc: {
@@ -537,6 +541,10 @@ export default define(meta, async (ps, me) => {
 		set.langs = ps.langs.filter(Boolean);
 	}
 
+	if (Array.isArray(ps.pinnedPages)) {
+		set.pinnedPages = ps.pinnedPages.filter(Boolean);
+	}
+
 	if (ps.summalyProxy !== undefined) {
 		set.summalyProxy = ps.summalyProxy;
 	}
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index f46139aa23..97376a9d73 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -99,8 +99,6 @@ export default define(meta, async (ps, me) => {
 		}
 	});
 
-	const proxyAccount = instance.proxyAccountId ? await Users.pack(instance.proxyAccountId).catch(() => null) : null;
-
 	const response: any = {
 		maintainerName: instance.maintainerName,
 		maintainerEmail: instance.maintainerEmail,
@@ -122,8 +120,6 @@ export default define(meta, async (ps, me) => {
 		disableGlobalTimeline: instance.disableGlobalTimeline,
 		driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
 		driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
-		cacheRemoteFiles: instance.cacheRemoteFiles,
-		proxyRemoteFiles: instance.proxyRemoteFiles,
 		enableHcaptcha: instance.enableHcaptcha,
 		hcaptchaSiteKey: instance.hcaptchaSiteKey,
 		enableRecaptcha: instance.enableRecaptcha,
@@ -135,9 +131,6 @@ export default define(meta, async (ps, me) => {
 		iconUrl: instance.iconUrl,
 		maxNoteTextLength: Math.min(instance.maxNoteTextLength, DB_MAX_NOTE_TEXT_LENGTH),
 		emojis: await Emojis.packMany(emojis),
-		requireSetup: (await Users.count({
-			host: null,
-		})) === 0,
 		enableEmail: instance.enableEmail,
 
 		enableTwitterIntegration: instance.enableTwitterIntegration,
@@ -146,10 +139,20 @@ export default define(meta, async (ps, me) => {
 
 		enableServiceWorker: instance.enableServiceWorker,
 
-		proxyAccountName: proxyAccount ? proxyAccount.username : null,
+		...(ps.detail ? {
+			pinnedPages: instance.pinnedPages,
+			cacheRemoteFiles: instance.cacheRemoteFiles,
+			proxyRemoteFiles: instance.proxyRemoteFiles,
+			requireSetup: (await Users.count({
+				host: null,
+			})) === 0,
+		} : {})
 	};
 
 	if (ps.detail) {
+		const proxyAccount = instance.proxyAccountId ? await Users.pack(instance.proxyAccountId).catch(() => null) : null;
+
+		response.proxyAccountName = proxyAccount ? proxyAccount.username : null;
 		response.features = {
 			registration: !instance.disableRegistration,
 			localTimeLine: !instance.disableLocalTimeline,
@@ -164,42 +167,42 @@ export default define(meta, async (ps, me) => {
 			serviceWorker: instance.enableServiceWorker,
 			miauth: true,
 		};
-	}
 
-	if (me && me.isAdmin) {
-		response.useStarForReactionFallback = instance.useStarForReactionFallback;
-		response.pinnedUsers = instance.pinnedUsers;
-		response.hiddenTags = instance.hiddenTags;
-		response.blockedHosts = instance.blockedHosts;
-		response.hcaptchaSecretKey = instance.hcaptchaSecretKey;
-		response.recaptchaSecretKey = instance.recaptchaSecretKey;
-		response.proxyAccountId = instance.proxyAccountId;
-		response.twitterConsumerKey = instance.twitterConsumerKey;
-		response.twitterConsumerSecret = instance.twitterConsumerSecret;
-		response.githubClientId = instance.githubClientId;
-		response.githubClientSecret = instance.githubClientSecret;
-		response.discordClientId = instance.discordClientId;
-		response.discordClientSecret = instance.discordClientSecret;
-		response.summalyProxy = instance.summalyProxy;
-		response.email = instance.email;
-		response.smtpSecure = instance.smtpSecure;
-		response.smtpHost = instance.smtpHost;
-		response.smtpPort = instance.smtpPort;
-		response.smtpUser = instance.smtpUser;
-		response.smtpPass = instance.smtpPass;
-		response.swPrivateKey = instance.swPrivateKey;
-		response.useObjectStorage = instance.useObjectStorage;
-		response.objectStorageBaseUrl = instance.objectStorageBaseUrl;
-		response.objectStorageBucket = instance.objectStorageBucket;
-		response.objectStoragePrefix = instance.objectStoragePrefix;
-		response.objectStorageEndpoint = instance.objectStorageEndpoint;
-		response.objectStorageRegion = instance.objectStorageRegion;
-		response.objectStoragePort = instance.objectStoragePort;
-		response.objectStorageAccessKey = instance.objectStorageAccessKey;
-		response.objectStorageSecretKey = instance.objectStorageSecretKey;
-		response.objectStorageUseSSL = instance.objectStorageUseSSL;
-		response.objectStorageUseProxy = instance.objectStorageUseProxy;
-		response.objectStorageSetPublicRead = instance.objectStorageSetPublicRead;
+		if (me && me.isAdmin) {
+			response.useStarForReactionFallback = instance.useStarForReactionFallback;
+			response.pinnedUsers = instance.pinnedUsers;
+			response.hiddenTags = instance.hiddenTags;
+			response.blockedHosts = instance.blockedHosts;
+			response.hcaptchaSecretKey = instance.hcaptchaSecretKey;
+			response.recaptchaSecretKey = instance.recaptchaSecretKey;
+			response.proxyAccountId = instance.proxyAccountId;
+			response.twitterConsumerKey = instance.twitterConsumerKey;
+			response.twitterConsumerSecret = instance.twitterConsumerSecret;
+			response.githubClientId = instance.githubClientId;
+			response.githubClientSecret = instance.githubClientSecret;
+			response.discordClientId = instance.discordClientId;
+			response.discordClientSecret = instance.discordClientSecret;
+			response.summalyProxy = instance.summalyProxy;
+			response.email = instance.email;
+			response.smtpSecure = instance.smtpSecure;
+			response.smtpHost = instance.smtpHost;
+			response.smtpPort = instance.smtpPort;
+			response.smtpUser = instance.smtpUser;
+			response.smtpPass = instance.smtpPass;
+			response.swPrivateKey = instance.swPrivateKey;
+			response.useObjectStorage = instance.useObjectStorage;
+			response.objectStorageBaseUrl = instance.objectStorageBaseUrl;
+			response.objectStorageBucket = instance.objectStorageBucket;
+			response.objectStoragePrefix = instance.objectStoragePrefix;
+			response.objectStorageEndpoint = instance.objectStorageEndpoint;
+			response.objectStorageRegion = instance.objectStorageRegion;
+			response.objectStoragePort = instance.objectStoragePort;
+			response.objectStorageAccessKey = instance.objectStorageAccessKey;
+			response.objectStorageSecretKey = instance.objectStorageSecretKey;
+			response.objectStorageUseSSL = instance.objectStorageUseSSL;
+			response.objectStorageUseProxy = instance.objectStorageUseProxy;
+			response.objectStorageSetPublicRead = instance.objectStorageSetPublicRead;
+		}
 	}
 
 	return response;
diff --git a/src/server/api/endpoints/notes/clips.ts b/src/server/api/endpoints/notes/clips.ts
new file mode 100644
index 0000000000..6126f12c66
--- /dev/null
+++ b/src/server/api/endpoints/notes/clips.ts
@@ -0,0 +1,54 @@
+import $ from 'cafy';
+import { ID } from '../../../../misc/cafy-id';
+import define from '../../define';
+import { ClipNotes, Clips } from '../../../../models';
+import { getNote } from '../../common/getters';
+import { ApiError } from '../../error';
+import { In } from 'typeorm';
+
+export const meta = {
+	tags: ['clips', 'notes'],
+
+	requireCredential: false as const,
+
+	params: {
+		noteId: {
+			validator: $.type(ID),
+		},
+	},
+
+	res: {
+		type: 'array' as const,
+		optional: false as const, nullable: false as const,
+		items: {
+			type: 'object' as const,
+			optional: false as const, nullable: false as const,
+			ref: 'Note',
+		}
+	},
+
+	errors: {
+		noSuchNote: {
+			message: 'No such note.',
+			code: 'NO_SUCH_NOTE',
+			id: '47db1a1c-b0af-458d-8fb4-986e4efafe1e'
+		}
+	}
+};
+
+export default define(meta, async (ps, me) => {
+	const note = await getNote(ps.noteId).catch(e => {
+		if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
+		throw e;
+	});
+
+	const clipNotes = await ClipNotes.find({
+		noteId: note.id,
+	});
+
+	const clips = await Clips.find({
+		id: In(clipNotes.map(x => x.clipId)),
+	});
+
+	return await Promise.all(clips.map(x => Clips.pack(x)));
+});
diff --git a/src/server/api/endpoints/pages/featured.ts b/src/server/api/endpoints/pages/featured.ts
new file mode 100644
index 0000000000..19802d0448
--- /dev/null
+++ b/src/server/api/endpoints/pages/featured.ts
@@ -0,0 +1,29 @@
+import define from '../../define';
+import { Pages } from '../../../../models';
+
+export const meta = {
+	tags: ['pages'],
+
+	requireCredential: false as const,
+
+	res: {
+		type: 'array' as const,
+		optional: false as const, nullable: false as const,
+		items: {
+			type: 'object' as const,
+			optional: false as const, nullable: false as const,
+			ref: 'Page',
+		}
+	},
+};
+
+export default define(meta, async (ps, me) => {
+	const query = Pages.createQueryBuilder('page')
+		.where('page.visibility = \'public\'')
+		.andWhere('page.likedCount > 0')
+		.orderBy('page.likedCount', 'DESC');
+
+	const pages = await query.take(10).getMany();
+
+	return await Pages.packMany(pages, me);
+});
diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index f889374139..0bc9f242ad 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -299,6 +299,7 @@ router.get('/@:user/pages/:page', async ctx => {
 });
 
 // Clip
+// TODO: 非publicなclipのハンドリング
 router.get('/clips/:clip', async ctx => {
 	const clip = await Clips.findOne({
 		id: ctx.params.clip,