diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22e09b2cac..b629fa4611 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ You should also include the user name that made the change.
 ### Improvements
 - ユーザーごとにRenoteをミュートできるように
 - ノートごとに絵文字リアクションを受け取るか設定できるように
+- ノート検索の利用可否をロールで制御可能に(デフォルトでオフ)
 - ロールの並び順を設定可能に
 - 指定した文字列を含む投稿の公開範囲をホームにできるように
 - enhance(client): 設定から自分のロールを確認できるように
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 992595734f..feb09b9054 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1261,6 +1261,7 @@ _role:
     rateLimitFactor: "レートリミット"
     descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
     canHideAds: "広告の非表示"
+    canSearchNotes: "ノート検索の利用可否"
   _condition:
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 7149591198..4775196c6f 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -21,6 +21,7 @@ export type RolePolicies = {
 	canPublicNote: boolean;
 	canInvite: boolean;
 	canManageCustomEmojis: boolean;
+	canSearchNotes: boolean;
 	canHideAds: boolean;
 	driveCapacityMb: number;
 	pinLimit: number;
@@ -40,6 +41,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
 	canPublicNote: true,
 	canInvite: false,
 	canManageCustomEmojis: false,
+	canSearchNotes: false,
 	canHideAds: false,
 	driveCapacityMb: 100,
 	pinLimit: 5,
@@ -264,6 +266,7 @@ export class RoleService implements OnApplicationShutdown {
 			canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
 			canInvite: calc('canInvite', vs => vs.some(v => v === true)),
 			canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
+			canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
 			canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
 			driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
 			pinLimit: calc('pinLimit', vs => Math.max(...vs)),
diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts
index ef47a3004d..5db5b6267f 100644
--- a/packages/backend/src/server/api/endpoints/notes/search.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search.ts
@@ -6,6 +6,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
+import { RoleService } from '@/core/RoleService.js';
+import { ApiError } from '../../error.js';
 
 export const meta = {
 	tags: ['notes'],
@@ -23,6 +25,11 @@ export const meta = {
 	},
 
 	errors: {
+		unavailable: {
+			message: 'Search of notes unavailable.',
+			code: 'UNAVAILABLE',
+			id: '0b44998d-77aa-4427-80d0-d2c9b8523011',
+		},
 	},
 } as const;
 
@@ -59,8 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 
 		private noteEntityService: NoteEntityService,
 		private queryService: QueryService,
+		private roleService: RoleService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const policies = await this.roleService.getUserPolicies(me ? me.id : null);
+			if (!policies.canSearchNotes) {
+				throw new ApiError(meta.errors.unavailable);
+			}
+	
 			const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId);
 
 			if (ps.userId) {
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 46ebc7d6a3..1d1b8fcea4 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -53,6 +53,7 @@ export const ROLE_POLICIES = [
 	'canPublicNote',
 	'canInvite',
 	'canManageCustomEmojis',
+	'canSearchNotes',
 	'canHideAds',
 	'driveCapacityMb',
 	'pinLimit',
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 408bbc6460..873ff02feb 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -187,6 +187,26 @@
 				</div>
 			</MkFolder>
 
+			<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])">
+				<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
+				<template #suffix>
+					<span v-if="role.policies.canSearchNotes.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+					<span v-else>{{ role.policies.canSearchNotes.value ? i18n.ts.yes : i18n.ts.no }}</span>
+					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canSearchNotes)"></i></span>
+				</template>
+				<div class="_gaps">
+					<MkSwitch v-model="role.policies.canSearchNotes.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
+					</MkSwitch>
+					<MkSwitch v-model="role.policies.canSearchNotes.value" :disabled="role.policies.canSearchNotes.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts.enable }}</template>
+					</MkSwitch>
+					<MkRange v-model="role.policies.canSearchNotes.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+						<template #label>{{ i18n.ts._role.priority }}</template>
+					</MkRange>
+				</div>
+			</MkFolder>
+
 			<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
 				<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
 				<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 25d8f3ad6e..351a44bec0 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -55,6 +55,14 @@
 							</MkSwitch>
 						</MkFolder>
 
+						<MkFolder>
+							<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
+							<template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.canSearchNotes">
+								<template #label>{{ i18n.ts.enable }}</template>
+							</MkSwitch>
+						</MkFolder>
+
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
 							<template #suffix>{{ policies.driveCapacityMb }}MB</template>
@@ -167,25 +175,7 @@ import { definePageMetadata } from '@/scripts/page-metadata';
 import { instance } from '@/instance';
 import { useRouter } from '@/router';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-
-const ROLE_POLICIES = [
-	'gtlAvailable',
-	'ltlAvailable',
-	'canPublicNote',
-	'canInvite',
-	'canManageCustomEmojis',
-	'canHideAds',
-	'driveCapacityMb',
-	'pinLimit',
-	'antennaLimit',
-	'wordMuteLimit',
-	'webhookLimit',
-	'clipLimit',
-	'noteEachClipsLimit',
-	'userListLimit',
-	'userEachUserListsLimit',
-	'rateLimitFactor',
-] as const;
+import { ROLE_POLICIES } from '@/const';
 
 const router = useRouter();