diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59149f59d1..bc77e01c14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,10 +26,11 @@
 -
 
 ### Server
+- フォローインポートなどでの大量のフォロー等操作をキューイングするように #10544 @nmkj-io
 - Misskey Webでのサーバーサイドエラー画面を改善
 - Misskey Webでのサーバーサイドエラーのログが残るように
 - ノート作成時のアンテナ追加パフォーマンスを改善
-- フォローインポートなどでの大量のフォロー等操作をキューイングするように #10544 @nmkj-io
+- アンテナとロールTLのuntil/sinceプロパティが動くように
 
 ## 13.11.2
 
diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index 072dab7ce2..eb18fb1b73 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -13,6 +13,7 @@ import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import type { Config } from '@/config.js';
 import { query } from '@/misc/prelude/url.js';
+import type { Serialized } from '@/server/api/stream/types.js';
 
 @Injectable()
 export class CustomEmojiService {
@@ -46,9 +47,9 @@ export class CustomEmojiService {
 			toRedisConverter: (value) => JSON.stringify(Array.from(value.values())),
 			fromRedisConverter: (value) => {
 				if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す)
-				return new Map(JSON.parse(value).map((x) => [x.name, {
+				return new Map(JSON.parse(value).map((x: Serialized<Emoji>) => [x.name, {
 					...x,
-					updatedAt: new Date(x.updatedAt),
+					updatedAt: x.updatedAt && new Date(x.updatedAt),
 				}]));
 			},
 		});
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index dacaa7263a..a8eded6733 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -20,6 +20,7 @@ import { bindThis } from '@/decorators.js';
 import { UserBlockingService } from '@/core/UserBlockingService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { CacheService } from '@/core/CacheService.js';
+import type { Config } from '@/config.js';
 import Logger from '../logger.js';
 
 const logger = new Logger('following/create');
@@ -44,6 +45,9 @@ export class UserFollowingService implements OnModuleInit {
 	constructor(
 		private moduleRef: ModuleRef,
 
+		@Inject(DI.config)
+		private config: Config,
+
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
 
@@ -411,7 +415,7 @@ export class UserFollowingService implements OnModuleInit {
 		}
 
 		if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
-			const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee));
+			const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee, requestId ?? `${this.config.url}/follows/${followRequest.id}`));
 			this.queueService.deliver(follower, content, followee.inbox, false);
 		}
 	}
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 5799622074..e13e9265ab 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -6,7 +6,7 @@ import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
 import accepts from 'accepts';
 import vary from 'vary';
 import { DI } from '@/di-symbols.js';
-import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js';
+import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/index.js';
 import * as url from '@/misc/prelude/url.js';
 import type { Config } from '@/config.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
@@ -54,6 +54,9 @@ export class ActivityPubServerService {
 		@Inject(DI.followingsRepository)
 		private followingsRepository: FollowingsRepository,
 
+		@Inject(DI.followRequestsRepository)
+		private followRequestsRepository: FollowRequestsRepository,
+
 		private utilityService: UtilityService,
 		private userEntityService: UserEntityService,
 		private apRendererService: ApRendererService,
@@ -205,22 +208,22 @@ export class ActivityPubServerService {
 			reply.code(400);
 			return;
 		}
-	
+
 		const page = request.query.page === 'true';
-	
+
 		const user = await this.usersRepository.findOneBy({
 			id: userId,
 			host: IsNull(),
 		});
-	
+
 		if (user == null) {
 			reply.code(404);
 			return;
 		}
-	
+
 		//#region Check ff visibility
 		const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
-	
+
 		if (profile.ffVisibility === 'private') {
 			reply.code(403);
 			reply.header('Cache-Control', 'public, max-age=30');
@@ -231,31 +234,31 @@ export class ActivityPubServerService {
 			return;
 		}
 		//#endregion
-	
+
 		const limit = 10;
 		const partOf = `${this.config.url}/users/${userId}/following`;
-	
+
 		if (page) {
 			const query = {
 				followerId: user.id,
 			} as FindOptionsWhere<Following>;
-	
+
 			// カーソルが指定されている場合
 			if (cursor) {
 				query.id = LessThan(cursor);
 			}
-	
+
 			// Get followings
 			const followings = await this.followingsRepository.find({
 				where: query,
 				take: limit + 1,
 				order: { id: -1 },
 			});
-	
+
 			// 「次のページ」があるかどうか
 			const inStock = followings.length === limit + 1;
 			if (inStock) followings.pop();
-	
+
 			const renderedFollowees = await Promise.all(followings.map(following => this.apRendererService.renderFollowUser(following.followeeId)));
 			const rendered = this.apRendererService.renderOrderedCollectionPage(
 				`${partOf}?${url.query({
@@ -269,7 +272,7 @@ export class ActivityPubServerService {
 					cursor: followings[followings.length - 1].id,
 				})}` : undefined,
 			);
-	
+
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		} else {
@@ -330,33 +333,33 @@ export class ActivityPubServerService {
 			reply.code(400);
 			return;
 		}
-	
+
 		const untilId = request.query.until_id;
 		if (untilId != null && typeof untilId !== 'string') {
 			reply.code(400);
 			return;
 		}
-	
+
 		const page = request.query.page === 'true';
-	
+
 		if (countIf(x => x != null, [sinceId, untilId]) > 1) {
 			reply.code(400);
 			return;
 		}
-	
+
 		const user = await this.usersRepository.findOneBy({
 			id: userId,
 			host: IsNull(),
 		});
-	
+
 		if (user == null) {
 			reply.code(404);
 			return;
 		}
-	
+
 		const limit = 20;
 		const partOf = `${this.config.url}/users/${userId}/outbox`;
-	
+
 		if (page) {
 			const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId)
 				.andWhere('note.userId = :userId', { userId: user.id })
@@ -365,11 +368,11 @@ export class ActivityPubServerService {
 					.orWhere('note.visibility = \'home\'');
 				}))
 				.andWhere('note.localOnly = FALSE');
-	
+
 			const notes = await query.take(limit).getMany();
-	
+
 			if (sinceId) notes.reverse();
-	
+
 			const activities = await Promise.all(notes.map(note => this.packActivity(note)));
 			const rendered = this.apRendererService.renderOrderedCollectionPage(
 				`${partOf}?${url.query({
@@ -387,7 +390,7 @@ export class ActivityPubServerService {
 					until_id: notes[notes.length - 1].id,
 				})}` : undefined,
 			);
-	
+
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		} else {
@@ -457,7 +460,7 @@ export class ActivityPubServerService {
 		// note
 		fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
 			vary(reply.raw, 'Accept');
-	
+
 			const note = await this.notesRepository.findOneBy({
 				id: request.params.note,
 				visibility: In(['public', 'home']),
@@ -639,6 +642,41 @@ export class ActivityPubServerService {
 			return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
 		});
 
+		// follow
+		fastify.get<{ Params: { followRequestId: string ; } }>('/follows/:followRequestId', async (request, reply) => {
+			// This may be used before the follow is completed, so we do not
+			// check if the following exists and only check if the follow request exists.
+
+			const followRequest = await this.followRequestsRepository.findOneBy({
+				id: request.params.followRequestId,
+			});
+
+			if (followRequest == null) {
+				reply.code(404);
+				return;
+			}
+
+			const [follower, followee] = await Promise.all([
+				this.usersRepository.findOneBy({
+					id: followRequest.followerId,
+					host: IsNull(),
+				}),
+				this.usersRepository.findOneBy({
+					id: followRequest.followeeId,
+					host: Not(IsNull()),
+				}),
+			]);
+
+			if (follower == null || followee == null) {
+				reply.code(404);
+				return;
+			}
+
+			reply.header('Cache-Control', 'public, max-age=180');
+			this.setResponseType(request, reply);
+			return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
+		});
+
 		done();
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index df83fe5f2a..88623ce26a 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -76,18 +76,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				throw new ApiError(meta.errors.noSuchAntenna);
 			}
 
-			const limit = ps.limit + (ps.untilId ? 1 : 0); // untilIdに指定したものも含まれるため+1
+			const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1
 			const noteIdsRes = await this.redisClient.xrevrange(
 				`antennaTimeline:${antenna.id}`,
-				ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : '+',
-				'-',
+				ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+',
+				ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-',
 				'COUNT', limit);
 
 			if (noteIdsRes.length === 0) {
 				return [];
 			}
 
-			const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId);
+			const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId);
 
 			if (noteIds.length === 0) {
 				return [];
diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts
index d79528593f..b45d4af1fe 100644
--- a/packages/backend/src/server/api/endpoints/roles/notes.ts
+++ b/packages/backend/src/server/api/endpoints/roles/notes.ts
@@ -71,18 +71,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				throw new ApiError(meta.errors.noSuchRole);
 			}
 
-			const limit = ps.limit + (ps.untilId ? 1 : 0); // untilIdに指定したものも含まれるため+1
+			const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1
 			const noteIdsRes = await this.redisClient.xrevrange(
 				`roleTimeline:${role.id}`,
-				ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : '+',
-				'-',
+				ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+',
+				ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-',
 				'COUNT', limit);
 
 			if (noteIdsRes.length === 0) {
 				return [];
 			}
 
-			const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId);
+			const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId);
 
 			if (noteIds.length === 0) {
 				return [];
diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts
index 101f6bf261..d9dba682cd 100644
--- a/packages/backend/src/server/api/stream/types.ts
+++ b/packages/backend/src/server/api/stream/types.ts
@@ -172,7 +172,7 @@ type EventUnionFromDictionary<
 > = U[keyof U];
 
 // redis通すとDateのインスタンスはstringに変換されるので
-type Serialized<T> = {
+export type Serialized<T> = {
 	[K in keyof T]:
 		T[K] extends Date
 			? string