diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts
index 9617f83880f1563402a157a6ac304ace6c122779..507fc464ff6a80001811f84a193b80ced6c77d4d 100644
--- a/packages/backend/src/core/FeaturedService.ts
+++ b/packages/backend/src/core/FeaturedService.ts
@@ -5,11 +5,12 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import * as Redis from 'ioredis';
-import type { MiNote, MiUser } from '@/models/_.js';
+import type { MiGalleryPost, MiNote, MiUser } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 
 const GLOBAL_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと
+export const GALLERY_POSTS_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと
 const PER_USER_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 7; // 1週間ごと
 const HASHTAG_RANKING_WINDOW = 1000 * 60 * 60; // 1時間ごと
 
@@ -79,6 +80,11 @@ export class FeaturedService {
 		return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score);
 	}
 
+	@bindThis
+	public updateGalleryPostsRanking(galleryPostId: MiGalleryPost['id'], score = 1): Promise<void> {
+		return this.updateRankingOf('featuredGalleryPostsRanking', GALLERY_POSTS_RANKING_WINDOW, galleryPostId, score);
+	}
+
 	@bindThis
 	public updateInChannelNotesRanking(channelId: MiNote['channelId'], noteId: MiNote['id'], score = 1): Promise<void> {
 		return this.updateRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, noteId, score);
@@ -99,6 +105,11 @@ export class FeaturedService {
 		return this.getRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, threshold);
 	}
 
+	@bindThis
+	public getGalleryPostsRanking(threshold: number): Promise<MiGalleryPost['id'][]> {
+		return this.getRankingOf('featuredGalleryPostsRanking', GALLERY_POSTS_RANKING_WINDOW, threshold);
+	}
+
 	@bindThis
 	public getInChannelNotesRanking(channelId: MiNote['channelId'], threshold: number): Promise<MiNote['id'][]> {
 		return this.getRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, threshold);
diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts
index cbab3a83a4108a882076811cea393c14acaea6c7..cea42340650b8f1e007aba19c0ce72336778caed 100644
--- a/packages/backend/src/server/api/endpoints/gallery/featured.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts
@@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { GalleryPostsRepository } from '@/models/_.js';
 import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
 import { DI } from '@/di-symbols.js';
+import { FeaturedService } from '@/core/FeaturedService.js';
 
 export const meta = {
 	tags: ['gallery'],
@@ -27,25 +28,49 @@ export const meta = {
 
 export const paramDef = {
 	type: 'object',
-	properties: {},
+	properties: {
+		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+		untilId: { type: 'string', format: 'misskey:id' },
+	},
 	required: [],
 } as const;
 
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	private galleryPostsRankingCache: string[] = [];
+	private galleryPostsRankingCacheLastFetchedAt = 0;
+
 	constructor(
 		@Inject(DI.galleryPostsRepository)
 		private galleryPostsRepository: GalleryPostsRepository,
 
 		private galleryPostEntityService: GalleryPostEntityService,
+		private featuredService: FeaturedService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			let postIds: string[];
+			if (this.galleryPostsRankingCacheLastFetchedAt !== 0 && (Date.now() - this.galleryPostsRankingCacheLastFetchedAt < 1000 * 60 * 30)) {
+				postIds = this.galleryPostsRankingCache;
+			} else {
+				postIds = await this.featuredService.getGalleryPostsRanking(100);
+				this.galleryPostsRankingCache = postIds;
+				this.galleryPostsRankingCacheLastFetchedAt = Date.now();
+			}
+
+			postIds.sort((a, b) => a > b ? -1 : 1);
+			if (ps.untilId) {
+				postIds = postIds.filter(id => id < ps.untilId!);
+			}
+			postIds = postIds.slice(0, ps.limit);
+
+			if (postIds.length === 0) {
+				return [];
+			}
+
 			const query = this.galleryPostsRepository.createQueryBuilder('post')
-				.andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) })
-				.andWhere('post.likedCount > 0')
-				.orderBy('post.likedCount', 'DESC');
+				.where('post.id IN (:...postIds)', { postIds: postIds });
 
-			const posts = await query.limit(10).getMany();
+			const posts = await query.getMany();
 
 			return await this.galleryPostEntityService.packMany(posts, me);
 		});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
index 561b2492ab5b4620cee7661c40ba3c8cfcb53df4..cc424261b49c3317e90a1c79ee8f30f61b130f24 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
@@ -6,6 +6,7 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/_.js';
+import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
 import { IdService } from '@/core/IdService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '../../../error.js';
@@ -57,6 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.galleryLikesRepository)
 		private galleryLikesRepository: GalleryLikesRepository,
 
+		private featuredService: FeaturedService,
 		private idService: IdService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
@@ -88,6 +90,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				userId: me.id,
 			});
 
+			// ランキング更新
+			if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
+				await this.featuredService.updateGalleryPostsRanking(post.id, 1);
+			}
+
 			this.galleryPostsRepository.increment({ id: post.id }, 'likedCount', 1);
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
index 832b62282fa5832fc374da66a138fb6e532c4f6f..caa4d455533810093cfb30939afad4846095690f 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
@@ -6,6 +6,8 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/_.js';
+import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
+import { IdService } from '@/core/IdService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '../../../error.js';
 
@@ -49,6 +51,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		@Inject(DI.galleryLikesRepository)
 		private galleryLikesRepository: GalleryLikesRepository,
+
+		private featuredService: FeaturedService,
+		private idService: IdService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId });
@@ -68,6 +73,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			// Delete like
 			await this.galleryLikesRepository.delete(exist.id);
 
+			// ランキング更新
+			if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
+				await this.featuredService.updateGalleryPostsRanking(post.id, -1);
+			}
+
 			this.galleryPostsRepository.decrement({ id: post.id }, 'likedCount', 1);
 		});
 	}