diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47a0083922..ceb7234866 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
 - API: notes/global-timeline は現在常に `[]` を返します
 
 ### General
+- ユーザーごとに他ユーザーへの返信をタイムラインに含めるか設定可能になりました
 - ソフトワードミュートとハードワードミュートは統合されました
 
 ### Server
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 62db216953..418e1c67ff 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1129,6 +1129,8 @@ export interface Locale {
     "notificationRecieveConfig": string;
     "mutualFollow": string;
     "fileAttachedOnly": string;
+    "showRepliesToOthersInTimeline": string;
+    "hideRepliesToOthersInTimeline": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index a6e9d9458e..80e4466a74 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1126,6 +1126,8 @@ edited: "編集済み"
 notificationRecieveConfig: "通知の受信設定"
 mutualFollow: "相互フォロー"
 fileAttachedOnly: "ファイル付きのみ"
+showRepliesToOthersInTimeline: "TLに他の人への返信を含める"
+hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
diff --git a/packages/backend/migration/1696222183852-withReplies.js b/packages/backend/migration/1696222183852-withReplies.js
new file mode 100644
index 0000000000..9f65d5f6a1
--- /dev/null
+++ b/packages/backend/migration/1696222183852-withReplies.js
@@ -0,0 +1,20 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class WithReplies1696222183852 {
+    name = 'WithReplies1696222183852'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "following" ADD "withReplies" boolean NOT NULL DEFAULT false`);
+        await queryRunner.query(`ALTER TABLE "user_list_joining" ADD "withReplies" boolean NOT NULL DEFAULT false`);
+        await queryRunner.query(`CREATE INDEX "IDX_d74d8ab5efa7e3bb82825c0fa2" ON "following" ("followeeId", "followerHost") `);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`DROP INDEX "public"."IDX_d74d8ab5efa7e3bb82825c0fa2"`);
+        await queryRunner.query(`ALTER TABLE "user_list_joining" DROP COLUMN "withReplies"`);
+        await queryRunner.query(`ALTER TABLE "following" DROP COLUMN "withReplies"`);
+    }
+}
diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts
index 561979c4bf..8191f611b0 100644
--- a/packages/backend/src/core/CacheService.ts
+++ b/packages/backend/src/core/CacheService.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import * as Redis from 'ioredis';
-import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
+import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiFollowing } from '@/models/_.js';
 import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
 import type { MiLocalUser, MiUser } from '@/models/User.js';
 import { DI } from '@/di-symbols.js';
@@ -25,7 +25,7 @@ export class CacheService implements OnApplicationShutdown {
 	public userBlockingCache: RedisKVCache<Set<string>>;
 	public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
 	public renoteMutingsCache: RedisKVCache<Set<string>>;
-	public userFollowingsCache: RedisKVCache<Set<string>>;
+	public userFollowingsCache: RedisKVCache<Record<string, Pick<MiFollowing, 'withReplies'> | undefined>>;
 	public userFollowingChannelsCache: RedisKVCache<Set<string>>;
 
 	constructor(
@@ -136,12 +136,18 @@ export class CacheService implements OnApplicationShutdown {
 			fromRedisConverter: (value) => new Set(JSON.parse(value)),
 		});
 
-		this.userFollowingsCache = new RedisKVCache<Set<string>>(this.redisClient, 'userFollowings', {
+		this.userFollowingsCache = new RedisKVCache<Record<string, Pick<MiFollowing, 'withReplies'> | undefined>>(this.redisClient, 'userFollowings', {
 			lifetime: 1000 * 60 * 30, // 30m
 			memoryCacheLifetime: 1000 * 60, // 1m
-			fetcher: (key) => this.followingsRepository.find({ where: { followerId: key }, select: ['followeeId'] }).then(xs => new Set(xs.map(x => x.followeeId))),
-			toRedisConverter: (value) => JSON.stringify(Array.from(value)),
-			fromRedisConverter: (value) => new Set(JSON.parse(value)),
+			fetcher: (key) => this.followingsRepository.find({ where: { followerId: key }, select: ['followeeId', 'withReplies'] }).then(xs => {
+				const obj: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
+				for (const x of xs) {
+					obj[x.followeeId] = { withReplies: x.withReplies };
+				}
+				return obj;
+			}),
+			toRedisConverter: (value) => JSON.stringify(value),
+			fromRedisConverter: (value) => JSON.parse(value),
 		});
 
 		this.userFollowingChannelsCache = new RedisKVCache<Set<string>>(this.redisClient, 'userFollowingChannels', {
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index f8342c0986..6fa9c9cf57 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -805,15 +805,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 	private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) {
 		const redisPipeline = this.redisClient.pipeline();
 
-		if (note.replyId && note.replyUserId !== note.userId) {
-			if (note.visibility === 'public' || note.visibility === 'home') {
-				redisPipeline.xadd(
-					`userTimelineWithReplies:${user.id}`,
-					'MAXLEN', '~', '200',
-					'*',
-					'note', note.id);
-			}
-		} else if (note.channelId) {
+		if (note.channelId) {
 			const channelFollowings = await this.channelFollowingsRepository.find({
 				where: {
 					followeeId: note.channelId,
@@ -845,7 +837,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 					followeeId: user.id,
 					followerHost: IsNull(),
 				},
-				select: ['followerId'],
+				select: ['followerId', 'withReplies'],
 			});
 
 			let userLists = await this.userListJoiningsRepository.find({
@@ -857,6 +849,11 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 			// TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする
 			for (const following of followings) {
+				// 自分自身以外への返信
+				if (note.replyId && note.replyUserId !== note.userId) {
+					if (!following.withReplies) continue;
+				}
+
 				redisPipeline.xadd(
 					`homeTimeline:${following.followerId}`,
 					'MAXLEN', '~', '200',
@@ -878,6 +875,11 @@ export class NoteCreateService implements OnApplicationShutdown {
 			}
 
 			for (const userList of userLists) {
+				// 自分自身以外への返信
+				if (note.replyId && note.replyUserId !== note.userId) {
+					if (!userList.withReplies) continue;
+				}
+
 				redisPipeline.xadd(
 					`userListTimeline:${userList.userListId}`,
 					'MAXLEN', '~', '200',
@@ -893,49 +895,60 @@ export class NoteCreateService implements OnApplicationShutdown {
 				}
 			}
 
-			redisPipeline.xadd(
-				`homeTimeline:${user.id}`,
-				'MAXLEN', '~', '200',
-				'*',
-				'note', note.id);
-
-			if (note.fileIds.length > 0) {
+			{ // 自分自身のHTL
 				redisPipeline.xadd(
-					`homeTimelineWithFiles:${user.id}`,
-					'MAXLEN', '~', '100',
-					'*',
-					'note', note.id);
-			}
-
-			if (note.visibility === 'public' || note.visibility === 'home') {
-				redisPipeline.xadd(
-					`userTimeline:${user.id}`,
+					`homeTimeline:${user.id}`,
 					'MAXLEN', '~', '200',
 					'*',
 					'note', note.id);
 
 				if (note.fileIds.length > 0) {
 					redisPipeline.xadd(
-						`userTimelineWithFiles:${user.id}`,
+						`homeTimelineWithFiles:${user.id}`,
 						'MAXLEN', '~', '100',
 						'*',
 						'note', note.id);
 				}
+			}
 
-				if (note.userHost == null) {
+			if (note.visibility === 'public' || note.visibility === 'home') {
+				// 自分自身以外への返信
+				if (note.replyId && note.replyUserId !== note.userId) {
 					redisPipeline.xadd(
-						'localTimeline',
-						'MAXLEN', '~', '1000',
+						`userTimelineWithReplies:${user.id}`,
+						'MAXLEN', '~', '200',
+						'*',
+						'note', note.id);
+				} else {
+					redisPipeline.xadd(
+						`userTimeline:${user.id}`,
+						'MAXLEN', '~', '200',
 						'*',
 						'note', note.id);
 
 					if (note.fileIds.length > 0) {
 						redisPipeline.xadd(
-							'localTimelineWithFiles',
-							'MAXLEN', '~', '500',
+							`userTimelineWithFiles:${user.id}`,
+							'MAXLEN', '~', '100',
 							'*',
 							'note', note.id);
 					}
+
+					if (note.userHost == null) {
+						redisPipeline.xadd(
+							'localTimeline',
+							'MAXLEN', '~', '1000',
+							'*',
+							'note', note.id);
+
+						if (note.fileIds.length > 0) {
+							redisPipeline.xadd(
+								'localTimelineWithFiles',
+								'MAXLEN', '~', '500',
+								'*',
+								'note', note.id);
+						}
+					}
 				}
 			}
 		}
diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts
index ca05989a4a..32d54d2576 100644
--- a/packages/backend/src/core/NotificationService.ts
+++ b/packages/backend/src/core/NotificationService.ts
@@ -99,19 +99,19 @@ export class NotificationService implements OnApplicationShutdown {
 			}
 
 			if (recieveConfig?.type === 'following') {
-				const isFollowing = await this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => followings.has(notifierId));
+				const isFollowing = await this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId));
 				if (!isFollowing) {
 					return null;
 				}
 			} else if (recieveConfig?.type === 'follower') {
-				const isFollower = await this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => followings.has(notifieeId));
+				const isFollower = await this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId));
 				if (!isFollower) {
 					return null;
 				}
 			} else if (recieveConfig?.type === 'mutualFollow') {
 				const [isFollowing, isFollower] = await Promise.all([
-					this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => followings.has(notifierId)),
-					this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => followings.has(notifieeId)),
+					this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)),
+					this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)),
 				]);
 				if (!isFollowing && !isFollower) {
 					return null;
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index a47b3d51ac..171dda8fdc 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -487,6 +487,7 @@ export class UserEntityService implements OnModuleInit {
 				isMuted: relation.isMuted,
 				isRenoteMuted: relation.isRenoteMuted,
 				notify: relation.following?.notify ?? 'none',
+				withReplies: relation.following?.withReplies ?? false,
 			} : {}),
 		} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
 
diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts
index 8c9f965fad..1fbcd695b8 100644
--- a/packages/backend/src/models/Following.ts
+++ b/packages/backend/src/models/Following.ts
@@ -9,6 +9,7 @@ import { MiUser } from './User.js';
 
 @Entity('following')
 @Index(['followerId', 'followeeId'], { unique: true })
+@Index(['followeeId', 'followerHost'])
 export class MiFollowing {
 	@PrimaryColumn(id())
 	public id: string;
@@ -45,6 +46,12 @@ export class MiFollowing {
 	@JoinColumn()
 	public follower: MiUser | null;
 
+	// タイムラインにその人のリプライまで含めるかどうか
+	@Column('boolean', {
+		default: false,
+	})
+	public withReplies: boolean;
+
 	@Index()
 	@Column('varchar', {
 		length: 32,
diff --git a/packages/backend/src/models/UserListJoining.ts b/packages/backend/src/models/UserListJoining.ts
index 4918f2f700..bb5eb311e3 100644
--- a/packages/backend/src/models/UserListJoining.ts
+++ b/packages/backend/src/models/UserListJoining.ts
@@ -44,4 +44,10 @@ export class MiUserListJoining {
 	})
 	@JoinColumn()
 	public userList: MiUserList | null;
+
+	// タイムラインにその人のリプライまで含めるかどうか
+	@Column('boolean', {
+		default: false,
+	})
+	public withReplies: boolean;
 }
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 0181ea50e8..57d2d976ff 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -277,6 +277,10 @@ export const packedUserDetailedNotMeOnlySchema = {
 			type: 'string',
 			nullable: false, optional: true,
 		},
+		withReplies: {
+			type: 'boolean',
+			nullable: false, optional: true,
+		},
 		//#endregion
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/following/update.ts b/packages/backend/src/server/api/endpoints/following/update.ts
index 25f393e517..db17d151df 100644
--- a/packages/backend/src/server/api/endpoints/following/update.ts
+++ b/packages/backend/src/server/api/endpoints/following/update.ts
@@ -57,8 +57,9 @@ export const paramDef = {
 	properties: {
 		userId: { type: 'string', format: 'misskey:id' },
 		notify: { type: 'string', enum: ['normal', 'none'] },
+		withReplies: { type: 'boolean' },
 	},
-	required: ['userId', 'notify'],
+	required: ['userId'],
 } as const;
 
 @Injectable()
@@ -98,7 +99,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			await this.followingsRepository.update({
 				id: exist.id,
 			}, {
-				notify: ps.notify === 'none' ? null : ps.notify,
+				notify: ps.notify != null ? (ps.notify === 'none' ? null : ps.notify) : undefined,
+				withReplies: ps.withReplies != null ? ps.withReplies : undefined,
 			});
 
 			return await this.userEntityService.pack(follower.id, me);
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index 6d8a2386d7..615e3e8c73 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -91,7 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return [];
 			}
 
-			const isFollowing = me ? (await this.cacheService.userFollowingsCache.fetch(me.id)).has(ps.userId) : false;
+			const isFollowing = me ? Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId) : false;
 
 			const query = this.notesRepository.createQueryBuilder('note')
 				.where('note.id IN (:...noteIds)', { noteIds: noteIds })
diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts
index a73071ea99..f981e63871 100644
--- a/packages/backend/src/server/api/stream/Connection.ts
+++ b/packages/backend/src/server/api/stream/Connection.ts
@@ -11,7 +11,7 @@ import type { NoteReadService } from '@/core/NoteReadService.js';
 import type { NotificationService } from '@/core/NotificationService.js';
 import { bindThis } from '@/decorators.js';
 import { CacheService } from '@/core/CacheService.js';
-import { MiUserProfile } from '@/models/_.js';
+import { MiFollowing, MiUserProfile } from '@/models/_.js';
 import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js';
 import type { ChannelsService } from './ChannelsService.js';
 import type { EventEmitter } from 'events';
@@ -30,7 +30,7 @@ export default class Connection {
 	private subscribingNotes: any = {};
 	private cachedNotes: Packed<'Note'>[] = [];
 	public userProfile: MiUserProfile | null = null;
-	public following: Set<string> = new Set();
+	public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
 	public followingChannels: Set<string> = new Set();
 	public userIdsWhoMeMuting: Set<string> = new Set();
 	public userIdsWhoBlockingMe: Set<string> = new Set();
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index 4bdf607336..f0ac50349c 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -18,7 +18,6 @@ class GlobalTimelineChannel extends Channel {
 	public readonly chName = 'globalTimeline';
 	public static shouldShare = true;
 	public static requireCredential = false;
-	private withReplies: boolean;
 	private withRenotes: boolean;
 
 	constructor(
@@ -38,7 +37,6 @@ class GlobalTimelineChannel extends Channel {
 		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
 		if (!policies.gtlAvailable) return;
 
-		this.withReplies = params.withReplies ?? false;
 		this.withRenotes = params.withRenotes ?? true;
 
 		// Subscribe events
@@ -64,7 +62,7 @@ class GlobalTimelineChannel extends Channel {
 		}
 
 		// 関係ない返信は除外
-		if (note.reply && !this.withReplies) {
+		if (note.reply && !this.following[note.userId]?.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index cea0c781e2..1c1b1c2ae4 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -16,7 +16,6 @@ class HomeTimelineChannel extends Channel {
 	public readonly chName = 'homeTimeline';
 	public static shouldShare = true;
 	public static requireCredential = true;
-	private withReplies: boolean;
 	private withRenotes: boolean;
 
 	constructor(
@@ -31,7 +30,6 @@ class HomeTimelineChannel extends Channel {
 
 	@bindThis
 	public async init(params: any) {
-		this.withReplies = params.withReplies ?? false;
 		this.withRenotes = params.withRenotes ?? true;
 
 		this.subscriber.on('notesStream', this.onNote);
@@ -43,7 +41,7 @@ class HomeTimelineChannel extends Channel {
 			if (!this.followingChannels.has(note.channelId)) return;
 		} else {
 			// その投稿のユーザーをフォローしていなかったら弾く
-			if ((this.user!.id !== note.userId) && !this.following.has(note.userId)) return;
+			if ((this.user!.id !== note.userId) && !Object.hasOwn(this.following, note.userId)) return;
 		}
 
 		// Ignore notes from instances the user has muted
@@ -73,7 +71,7 @@ class HomeTimelineChannel extends Channel {
 		}
 
 		// 関係ない返信は除外
-		if (note.reply && !this.withReplies) {
+		if (note.reply && !this.following[note.userId]?.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index 0ad26ecee3..e2f4817bfa 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -18,7 +18,6 @@ class HybridTimelineChannel extends Channel {
 	public readonly chName = 'hybridTimeline';
 	public static shouldShare = true;
 	public static requireCredential = true;
-	private withReplies: boolean;
 	private withRenotes: boolean;
 
 	constructor(
@@ -38,7 +37,6 @@ class HybridTimelineChannel extends Channel {
 		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
 		if (!policies.ltlAvailable) return;
 
-		this.withReplies = params.withReplies ?? false;
 		this.withRenotes = params.withRenotes ?? true;
 
 		// Subscribe events
@@ -53,7 +51,7 @@ class HybridTimelineChannel extends Channel {
 		// フォローしているチャンネルの投稿 の場合だけ
 		if (!(
 			(note.channelId == null && this.user!.id === note.userId) ||
-			(note.channelId == null && this.following.has(note.userId)) ||
+			(note.channelId == null && Object.hasOwn(this.following, note.userId)) ||
 			(note.channelId == null && (note.user.host == null && note.visibility === 'public')) ||
 			(note.channelId != null && this.followingChannels.has(note.channelId))
 		)) return;
@@ -85,7 +83,7 @@ class HybridTimelineChannel extends Channel {
 		if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances ?? []))) return;
 
 		// 関係ない返信は除外
-		if (note.reply && !this.withReplies) {
+		if (note.reply && !this.following[note.userId]?.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts
index 641cd0932b..ca563b5d19 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -17,7 +17,6 @@ class LocalTimelineChannel extends Channel {
 	public readonly chName = 'localTimeline';
 	public static shouldShare = true;
 	public static requireCredential = false;
-	private withReplies: boolean;
 	private withRenotes: boolean;
 
 	constructor(
@@ -37,7 +36,6 @@ class LocalTimelineChannel extends Channel {
 		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
 		if (!policies.ltlAvailable) return;
 
-		this.withReplies = params.withReplies ?? false;
 		this.withRenotes = params.withRenotes ?? true;
 
 		// Subscribe events
@@ -64,7 +62,7 @@ class LocalTimelineChannel extends Channel {
 		}
 
 		// 関係ない返信は除外
-		if (note.reply && this.user && !this.withReplies) {
+		if (note.reply && this.user && !this.following[note.userId]?.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return;
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 0f5d5f7344..53db1ac28a 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -133,6 +133,7 @@ describe('ユーザー', () => {
 			isMuted: user.isMuted ?? false,
 			isRenoteMuted: user.isRenoteMuted ?? false,
 			notify: user.notify ?? 'none',
+			withReplies: user.withReplies ?? false,
 		});
 	};
 
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 1dcafd6be1..c4a34667ef 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -23,11 +23,9 @@ const props = withDefaults(defineProps<{
 	role?: string;
 	sound?: boolean;
 	withRenotes?: boolean;
-	withReplies?: boolean;
 	onlyFiles?: boolean;
 }>(), {
 	withRenotes: true,
-	withReplies: false,
 	onlyFiles: false,
 });
 
@@ -70,12 +68,10 @@ if (props.src === 'antenna') {
 	endpoint = 'notes/timeline';
 	query = {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	};
 	connection = stream.useChannel('homeTimeline', {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	});
 	connection.on('note', prepend);
@@ -85,12 +81,10 @@ if (props.src === 'antenna') {
 	endpoint = 'notes/local-timeline';
 	query = {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	};
 	connection = stream.useChannel('localTimeline', {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	});
 	connection.on('note', prepend);
@@ -98,12 +92,10 @@ if (props.src === 'antenna') {
 	endpoint = 'notes/hybrid-timeline';
 	query = {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	};
 	connection = stream.useChannel('hybridTimeline', {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	});
 	connection.on('note', prepend);
@@ -111,12 +103,10 @@ if (props.src === 'antenna') {
 	endpoint = 'notes/global-timeline';
 	query = {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	};
 	connection = stream.useChannel('globalTimeline', {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 	});
 	connection.on('note', prepend);
@@ -140,13 +130,11 @@ if (props.src === 'antenna') {
 	endpoint = 'notes/user-list-timeline';
 	query = {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 		listId: props.list,
 	};
 	connection = stream.useChannel('userList', {
 		withRenotes: props.withRenotes,
-		withReplies: props.withReplies,
 		withFiles: props.onlyFiles ? true : undefined,
 		listId: props.list,
 	});
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 9cafdc3dd7..b8deb77952 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -15,11 +15,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div :class="$style.tl">
 				<MkTimeline
 					ref="tlComponent"
-					:key="src + withRenotes + withReplies + onlyFiles"
+					:key="src + withRenotes + onlyFiles"
 					:src="src.split(':')[0]"
 					:list="src.split(':')[1]"
 					:withRenotes="withRenotes"
-					:withReplies="withReplies"
 					:onlyFiles="onlyFiles"
 					:sound="true"
 					@queue="queueUpdated"
@@ -62,7 +61,6 @@ let queue = $ref(0);
 let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global');
 const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) });
 const withRenotes = $ref(true);
-const withReplies = $ref(false);
 const onlyFiles = $ref(false);
 
 watch($$(src), () => queue = 0);
@@ -144,12 +142,7 @@ const headerActions = $computed(() => [{
 			text: i18n.ts.showRenotes,
 			icon: 'ti ti-repeat',
 			ref: $$(withRenotes),
-		}, /*{
-			type: 'switch',
-			text: i18n.ts.withReplies,
-			icon: 'ti ti-arrow-back-up',
-			ref: $$(withReplies),
-		},*/ {
+		}, {
 			type: 'switch',
 			text: i18n.ts.fileAttachedOnly,
 			icon: 'ti ti-photo',
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 128cbafb15..be514be5b1 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -80,6 +80,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 		});
 	}
 
+	async function toggleWithReplies() {
+		os.apiWithDialog('following/update', {
+			userId: user.id,
+			withReplies: !user.withReplies,
+		}).then(() => {
+			user.withReplies = !user.withReplies;
+		});
+	}
+
 	async function toggleNotify() {
 		os.apiWithDialog('following/update', {
 			userId: user.id,
@@ -282,6 +291,10 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 		// フォローしたとしても user.isFollowing はリアルタイム更新されないので不便なため
 		//if (user.isFollowing) {
 		menu = menu.concat([{
+			icon: user.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
+			text: user.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline,
+			action: toggleWithReplies,
+		}, {
 			icon: user.notify === 'none' ? 'ti ti-bell' : 'ti ti-bell-off',
 			text: user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes,
 			action: toggleNotify,
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 49fdf4d314..b2a44ac96b 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -31,7 +31,6 @@ export type Column = {
 	excludeTypes?: typeof notificationTypes[number][];
 	tl?: 'home' | 'local' | 'social' | 'global';
 	withRenotes?: boolean;
-	withReplies?: boolean;
 	onlyFiles?: boolean;
 };
 
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index d80d8f3d90..847752247e 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -23,10 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkTimeline
 		v-else-if="column.tl"
 		ref="timeline"
-		:key="column.tl + withRenotes + withReplies + onlyFiles"
+		:key="column.tl + withRenotes + onlyFiles"
 		:src="column.tl"
 		:withRenotes="withRenotes"
-		:withReplies="withReplies"
 		:onlyFiles="onlyFiles"
 	/>
 </XColumn>
@@ -52,7 +51,6 @@ let disabled = $ref(false);
 const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
 const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
 const withRenotes = $ref(props.column.withRenotes ?? true);
-const withReplies = $ref(props.column.withReplies ?? false);
 const onlyFiles = $ref(props.column.onlyFiles ?? false);
 
 watch($$(withRenotes), v => {
@@ -61,12 +59,6 @@ watch($$(withRenotes), v => {
 	});
 });
 
-watch($$(withReplies), v => {
-	updateColumn(props.column.id, {
-		withReplies: v,
-	});
-});
-
 watch($$(onlyFiles), v => {
 	updateColumn(props.column.id, {
 		onlyFiles: v,
@@ -115,11 +107,7 @@ const menu = [{
 	type: 'switch',
 	text: i18n.ts.showRenotes,
 	ref: $$(withRenotes),
-}, /*{
-	type: 'switch',
-	text: i18n.ts.withReplies,
-	ref: $$(withReplies),
-},*/ {
+}, {
 	type: 'switch',
 	text: i18n.ts.fileAttachedOnly,
 	ref: $$(onlyFiles),