diff --git a/app/assets/javascripts/admin/users/components/actions/index.js b/app/assets/javascripts/admin/users/components/actions/index.js
index 4e63a85df891f247ad9353718fb40c4682bf3737..633bc4d8b15c32793d8a64bad40e6d038d7380d2 100644
--- a/app/assets/javascripts/admin/users/components/actions/index.js
+++ b/app/assets/javascripts/admin/users/components/actions/index.js
@@ -9,6 +9,8 @@ import Reject from './reject.vue';
 import Unban from './unban.vue';
 import Unblock from './unblock.vue';
 import Unlock from './unlock.vue';
+import Trust from './trust_user.vue';
+import Untrust from './untrust_user.vue';
 
 export default {
   Activate,
@@ -22,4 +24,6 @@ export default {
   Unblock,
   Unlock,
   Reject,
+  Trust,
+  Untrust,
 };
diff --git a/app/assets/javascripts/admin/users/components/actions/trust_user.vue b/app/assets/javascripts/admin/users/components/actions/trust_user.vue
new file mode 100644
index 0000000000000000000000000000000000000000..41ff8d4120de27f8e529151bfc1bb0a848447332
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/trust_user.vue
@@ -0,0 +1,62 @@
+<script>
+import { GlDisclosureDropdownItem } from '@gitlab/ui';
+import { sprintf, s__, __ } from '~/locale';
+import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
+import { I18N_USER_ACTIONS } from '../../constants';
+
+// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
+const messageHtml = `
+  <p>${s__('AdminUsers|When not being monitored for spam:')}</p>
+  <ul>
+    <li>${s__(
+      'AdminUsers|The user can create issues, notes, snippets, and merge requests that appear to be spam without being blocked.',
+    )}</li>
+  </ul>
+  <p>${s__('AdminUsers|You can untrust this user in the future.')}</p>
+`;
+
+export default {
+  components: {
+    GlDisclosureDropdownItem,
+  },
+  props: {
+    username: {
+      type: String,
+      required: true,
+    },
+    path: {
+      type: String,
+      required: true,
+    },
+  },
+  methods: {
+    onClick() {
+      eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
+        path: this.path,
+        method: 'put',
+        modalAttributes: {
+          title: sprintf(s__('AdminUsers|Stop monitoring %{username} for possible spam?'), {
+            username: this.username,
+          }),
+          actionCancel: {
+            text: __('Cancel'),
+          },
+          actionPrimary: {
+            text: I18N_USER_ACTIONS.trust,
+            attributes: { variant: 'confirm' },
+          },
+          messageHtml,
+        },
+      });
+    },
+  },
+};
+</script>
+
+<template>
+  <gl-disclosure-dropdown-item @action="onClick">
+    <template #list-item>
+      <slot></slot>
+    </template>
+  </gl-disclosure-dropdown-item>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/actions/untrust_user.vue b/app/assets/javascripts/admin/users/components/actions/untrust_user.vue
new file mode 100644
index 0000000000000000000000000000000000000000..da59833af07596d877b613fa26cbf026b9fe3069
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/actions/untrust_user.vue
@@ -0,0 +1,56 @@
+<script>
+import { GlDisclosureDropdownItem } from '@gitlab/ui';
+import { sprintf, s__, __ } from '~/locale';
+import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
+import { I18N_USER_ACTIONS } from '../../constants';
+
+// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
+const messageHtml = `<p>${s__(
+  'AdminUsers|You can trust this user in the future if necessary.',
+)}</p>`;
+
+export default {
+  components: {
+    GlDisclosureDropdownItem,
+  },
+  props: {
+    username: {
+      type: String,
+      required: true,
+    },
+    path: {
+      type: String,
+      required: true,
+    },
+  },
+  methods: {
+    onClick() {
+      eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
+        path: this.path,
+        method: 'put',
+        modalAttributes: {
+          title: sprintf(s__('AdminUsers|Re-enable spam monitoring for %{username}?'), {
+            username: this.username,
+          }),
+          actionCancel: {
+            text: __('Cancel'),
+          },
+          actionPrimary: {
+            text: I18N_USER_ACTIONS.untrust,
+            attributes: { variant: 'confirm' },
+          },
+          messageHtml,
+        },
+      });
+    },
+  },
+};
+</script>
+
+<template>
+  <gl-disclosure-dropdown-item @action="onClick">
+    <template #list-item>
+      <slot></slot>
+    </template>
+  </gl-disclosure-dropdown-item>
+</template>
diff --git a/app/assets/javascripts/admin/users/constants.js b/app/assets/javascripts/admin/users/constants.js
index 9cd61d6b1dbcc676fa2ae8201224c0a3c3bc11da..43c9a8749cde76f38d4d2b5f47c8631534322cad 100644
--- a/app/assets/javascripts/admin/users/constants.js
+++ b/app/assets/javascripts/admin/users/constants.js
@@ -19,4 +19,6 @@ export const I18N_USER_ACTIONS = {
   deleteWithContributions: s__('AdminUsers|Delete user and contributions'),
   ban: s__('AdminUsers|Ban user'),
   unban: s__('AdminUsers|Unban user'),
+  trust: s__('AdminUsers|Trust user'),
+  untrust: s__('AdminUsers|Untrust user'),
 };
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 9634257209daf418b0cf504d7cb8bb4744d9e3fc..ee78d5a8c35f46c4e4f08613d525e3510c3bcf6e 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -310,7 +310,7 @@ def paginate_without_count?
   end
 
   def users_with_included_associations(users)
-    users.includes(:authorized_projects) # rubocop: disable CodeReuse/ActiveRecord
+    users.includes(:authorized_projects, :trusted_with_spam_attribute) # rubocop: disable CodeReuse/ActiveRecord
   end
 
   def admin_making_changes_for_another_user?
diff --git a/app/helpers/admin/user_actions_helper.rb b/app/helpers/admin/user_actions_helper.rb
index 969c5d5a0b5b1432deba0a9d6437b758c0dc3849..ba40b3c8a8df5a6f639ce153ee85e35797f59cd9 100644
--- a/app/helpers/admin/user_actions_helper.rb
+++ b/app/helpers/admin/user_actions_helper.rb
@@ -16,6 +16,7 @@ def admin_actions(user)
       unlock_actions
       delete_actions
       ban_actions
+      trust_actions
 
       @actions
     end
@@ -66,5 +67,19 @@ def ban_actions
         @actions << 'ban'
       end
     end
+
+    def trust_actions
+      return if @user.internal? ||
+        @user.blocked_pending_approval? ||
+        @user.banned? ||
+        @user.blocked? ||
+        @user.deactivated?
+
+      @actions << if @user.trusted?
+                    'untrust'
+                  else
+                    'trust'
+                  end
+    end
   end
 end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index a892b6e6ac6f7c39958cffe10398663aad366f49..81c41aee142a70af986d39f924d8631946303ba9 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -262,7 +262,9 @@ def admin_users_paths
       delete_with_contributions: admin_user_path(:id, hard_delete: true),
       admin_user: admin_user_path(:id),
       ban: ban_admin_user_path(:id),
-      unban: unban_admin_user_path(:id)
+      unban: unban_admin_user_path(:id),
+      trust: trust_admin_user_path(:id),
+      untrust: untrust_admin_user_path(:id)
     }
   end
 
diff --git a/app/models/user.rb b/app/models/user.rb
index fdc0b531521bc1505edf5d788daafa7480f52c40..bc256ef0f310feba437bd062d14f5e84fcba3f93 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -601,6 +601,12 @@ def blocked?
   scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
   scope :by_ids_or_usernames, -> (ids, usernames) { where(username: usernames).or(where(id: ids)) }
   scope :without_forbidden_states, -> { where.not(state: FORBIDDEN_SEARCH_STATES) }
+  scope :trusted, -> do
+    where('EXISTS (?)', ::UserCustomAttribute
+      .select(1)
+      .where('user_id = users.id')
+      .trusted_with_spam)
+  end
 
   strip_attributes! :name
 
@@ -769,6 +775,8 @@ def filter_items(filter_name)
         external
       when 'deactivated'
         deactivated
+      when "trusted"
+        trusted
       else
         active_without_ghosts
       end
diff --git a/app/views/admin/users/_users.html.haml b/app/views/admin/users/_users.html.haml
index d4a9009a0cf1b7d067981b80eca0c382b1ab4391..bbb068c3680e21d379ff29bd15b473baab795048 100644
--- a/app/views/admin/users/_users.html.haml
+++ b/app/views/admin/users/_users.html.haml
@@ -44,6 +44,9 @@
       = gl_tab_link_to admin_users_path(filter: "wop"), { item_active: active_when(params[:filter] == 'wop'), class: 'gl-border-0!' } do
         = s_('AdminUsers|Without projects')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.without_projects))
+      = gl_tab_link_to admin_users_path(filter: "trusted"), { item_active: active_when(params[:filter] == 'trusted'), class: 'gl-border-0!' } do
+        = s_('AdminUsers|Trusted')
+        = gl_tab_counter_badge(limited_counter_with_delimiter(User.trusted))
   .nav-controls
     = render_if_exists 'admin/users/admin_email_users'
     = render_if_exists 'admin/users/admin_export_user_permissions'
diff --git a/doc/administration/moderate_users.md b/doc/administration/moderate_users.md
index b30294c5fe0bd5be4c319ed78f93934cf42cd4cb..c12eb2b9a95045fccbafc6ab0231f787b160065d 100644
--- a/doc/administration/moderate_users.md
+++ b/doc/administration/moderate_users.md
@@ -287,6 +287,45 @@ You can also delete a user and their contributions, such as merge requests, issu
 NOTE:
 Before 15.1, additionally groups of which deleted user were the only owner among direct members were deleted.
 
+## Trust and untrust users
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132402) in GitLab 16.5.
+
+You can trust and untrust users from the Admin Area.
+
+By default, a user is not trusted and is blocked from creating issues, notes, and snippets considered to be spam. When you trust a user, they can create issues, notes, and snippets without being blocked.
+
+Prerequisite:
+
+- You must be an administrator.
+
+::Tabs
+
+:::TabTitle Trust a user
+
+1. On the left sidebar, select **Search or go to**.
+1. Select **Admin Area**.
+1. Select **Overview > Users**.
+1. Select a user.
+1. From the **User administration** dropdown list, select **Trust user**.
+1. On the confirmation dialog, select **Trust user**.
+
+The user is trusted.
+
+:::TabTitle Untrust a user
+
+1. On the left sidebar, select **Search or go to**.
+1. Select **Admin Area**.
+1. Select **Overview > Users**.
+1. Select the **Trusted** tab.
+1. Select a user.
+1. From the **User administration** dropdown list, select **Untrust user**.
+1. On the confirmation dialog, select **Untrust user**.
+
+The user is untrusted.
+
+::EndTabs
+
 ## Troubleshooting
 
 When moderating users, you may need to perform bulk actions on them based on certain conditions. The following rails console scripts show some examples of this. You may [start a rails console session](../administration/operations/rails_console.md#starting-a-rails-console-session) and use scripts similar to the following:
diff --git a/doc/administration/review_spam_logs.md b/doc/administration/review_spam_logs.md
index 35cc78a9bf36d3402d6fc4524b5d287db1194ae5..e3b96cdae9505978c7af6577c8b4db955529c774 100644
--- a/doc/administration/review_spam_logs.md
+++ b/doc/administration/review_spam_logs.md
@@ -38,15 +38,3 @@ You can resolve a spam log with one of the following effects:
 NOTE:
 Users can be [blocked](../api/users.md#block-user) and
 [unblocked](../api/users.md#unblock-user) using the GitLab API.
-
-<!-- ## Troubleshooting
-
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
-
-Each scenario can be a third-level heading, for example `### Getting error message X`.
-If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. -->
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6f1a247ba6c5e6915db7a19f8770cec23c17b33d..5e3e31414de33532daab6dd288882d7a8391776a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -4056,6 +4056,9 @@ msgstr ""
 msgid "AdminUsers|Projects, issues, merge requests, and comments of this user are hidden from other users."
 msgstr ""
 
+msgid "AdminUsers|Re-enable spam monitoring for %{username}?"
+msgstr ""
+
 msgid "AdminUsers|Reactivating a user will:"
 msgstr ""
 
@@ -4095,9 +4098,15 @@ msgstr ""
 msgid "AdminUsers|Sort by"
 msgstr ""
 
+msgid "AdminUsers|Stop monitoring %{username} for possible spam?"
+msgstr ""
+
 msgid "AdminUsers|The maximum compute minutes that jobs in this namespace can use on shared runners each month. Set 0 for unlimited. Set empty to inherit the global setting of %{minutes}"
 msgstr ""
 
+msgid "AdminUsers|The user can create issues, notes, snippets, and merge requests that appear to be spam without being blocked."
+msgstr ""
+
 msgid "AdminUsers|The user can't access git repositories."
 msgstr ""
 
@@ -4128,6 +4137,12 @@ msgstr ""
 msgid "AdminUsers|To confirm, type %{username}."
 msgstr ""
 
+msgid "AdminUsers|Trust user"
+msgstr ""
+
+msgid "AdminUsers|Trusted"
+msgstr ""
+
 msgid "AdminUsers|Unban user"
 msgstr ""
 
@@ -4143,6 +4158,9 @@ msgstr ""
 msgid "AdminUsers|Unlock user %{username}?"
 msgstr ""
 
+msgid "AdminUsers|Untrust user"
+msgstr ""
+
 msgid "AdminUsers|User administration"
 msgstr ""
 
@@ -4176,6 +4194,9 @@ msgstr ""
 msgid "AdminUsers|When banned:"
 msgstr ""
 
+msgid "AdminUsers|When not being monitored for spam:"
+msgstr ""
+
 msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
 msgstr ""
 
@@ -4206,9 +4227,15 @@ msgstr ""
 msgid "AdminUsers|You can ban their account in the future if necessary."
 msgstr ""
 
+msgid "AdminUsers|You can trust this user in the future if necessary."
+msgstr ""
+
 msgid "AdminUsers|You can unban their account in the future. Their data remains intact."
 msgstr ""
 
+msgid "AdminUsers|You can untrust this user in the future."
+msgstr ""
+
 msgid "AdminUsers|You cannot remove your own administrator access."
 msgstr ""
 
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index ca08bc9e577da3c03a00262943dbb7e808bbf081..9ab5b1fd3bb8758cbc7ee70f1005d2bac377746b 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -82,4 +82,12 @@
       end
     end
   end
+
+  it 'does not perform N+1 queries' do
+    control_queries = ActiveRecord::QueryRecorder.new { visit admin_users_path }
+
+    expect { create(:user) }.to change { User.count }.by(1)
+
+    expect { visit admin_users_path }.not_to exceed_query_limit(control_queries)
+  end
 end
diff --git a/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json b/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json
index 44d8e48a972c485fbc354521e5361a6091254f5c..61472b273e13422eb6ba7b28572daba32d316ae3 100644
--- a/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json
+++ b/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json
@@ -1,19 +1,51 @@
 {
   "type": "object",
   "properties": {
-    "edit": { "type": "string" },
-    "approve": { "type": "string" },
-    "reject": { "type": "string" },
-    "unblock": { "type": "string" },
-    "block": { "type": "string" },
-    "deactivate": { "type": "string" },
-    "activate": { "type": "string" },
-    "unlock": { "type": "string" },
-    "delete": { "type": "string" },
-    "delete_with_contributions": { "type": "string" },
-    "admin_user": { "type": "string" },
-    "ban": { "type": "string" },
-    "unban": { "type": "string" }
+    "edit": {
+      "type": "string"
+    },
+    "approve": {
+      "type": "string"
+    },
+    "reject": {
+      "type": "string"
+    },
+    "unblock": {
+      "type": "string"
+    },
+    "block": {
+      "type": "string"
+    },
+    "deactivate": {
+      "type": "string"
+    },
+    "activate": {
+      "type": "string"
+    },
+    "unlock": {
+      "type": "string"
+    },
+    "delete": {
+      "type": "string"
+    },
+    "delete_with_contributions": {
+      "type": "string"
+    },
+    "admin_user": {
+      "type": "string"
+    },
+    "ban": {
+      "type": "string"
+    },
+    "unban": {
+      "type": "string"
+    },
+    "trust": {
+      "type": "string"
+    },
+    "untrust": {
+      "type": "string"
+    }
   },
   "required": [
     "edit",
@@ -28,7 +60,9 @@
     "delete_with_contributions",
     "admin_user",
     "ban",
-    "unban"
+    "unban",
+    "trust",
+    "untrust"
   ],
   "additionalProperties": false
 }
diff --git a/spec/frontend/admin/users/constants.js b/spec/frontend/admin/users/constants.js
index d341eb03b1b33c6426ae1a50b65be5fffbbebdf0..39e8e51f43ca0bb9b196d3346e06c185dc1f6458 100644
--- a/spec/frontend/admin/users/constants.js
+++ b/spec/frontend/admin/users/constants.js
@@ -9,6 +9,8 @@ const REJECT = 'reject';
 const APPROVE = 'approve';
 const BAN = 'ban';
 const UNBAN = 'unban';
+const TRUST = 'trust';
+const UNTRUST = 'untrust';
 
 export const EDIT = 'edit';
 
@@ -24,6 +26,8 @@ export const CONFIRMATION_ACTIONS = [
   UNBAN,
   APPROVE,
   REJECT,
+  TRUST,
+  UNTRUST,
 ];
 
 export const DELETE_ACTIONS = [DELETE, DELETE_WITH_CONTRIBUTIONS];
diff --git a/spec/helpers/admin/user_actions_helper_spec.rb b/spec/helpers/admin/user_actions_helper_spec.rb
index 87d2308690c6175136baf0ee9c9c36034f56f174..abfdabf3413df96be4a3821f5bd4d8b30368518f 100644
--- a/spec/helpers/admin/user_actions_helper_spec.rb
+++ b/spec/helpers/admin/user_actions_helper_spec.rb
@@ -2,7 +2,7 @@
 
 require "spec_helper"
 
-RSpec.describe Admin::UserActionsHelper do
+RSpec.describe Admin::UserActionsHelper, feature_category: :user_management do
   describe '#admin_actions' do
     let_it_be(:current_user) { build(:user) }
 
@@ -29,13 +29,33 @@
     context 'the user is a standard user' do
       let_it_be(:user) { create(:user) }
 
-      it { is_expected.to contain_exactly("edit", "block", "ban", "deactivate", "delete", "delete_with_contributions") }
+      it do
+        is_expected.to contain_exactly(
+          "edit",
+          "block",
+          "ban",
+          "deactivate",
+          "delete",
+          "delete_with_contributions",
+          "trust"
+        )
+      end
     end
 
     context 'the user is an admin user' do
       let_it_be(:user) { create(:user, :admin) }
 
-      it { is_expected.to contain_exactly("edit", "block", "ban", "deactivate", "delete", "delete_with_contributions") }
+      it do
+        is_expected.to contain_exactly(
+          "edit",
+          "block",
+          "ban",
+          "deactivate",
+          "delete",
+          "delete_with_contributions",
+          "trust"
+        )
+      end
     end
 
     context 'the user is blocked by LDAP' do
@@ -59,7 +79,16 @@
     context 'the user is deactivated' do
       let_it_be(:user) { create(:user, :deactivated) }
 
-      it { is_expected.to contain_exactly("edit", "block", "ban", "activate", "delete", "delete_with_contributions") }
+      it do
+        is_expected.to contain_exactly(
+          "edit",
+          "block",
+          "ban",
+          "activate",
+          "delete",
+          "delete_with_contributions"
+        )
+      end
     end
 
     context 'the user is locked' do
@@ -77,7 +106,8 @@
           "deactivate",
           "unlock",
           "delete",
-          "delete_with_contributions"
+          "delete_with_contributions",
+          "trust"
         )
       }
     end
@@ -88,6 +118,21 @@
       it { is_expected.to contain_exactly("edit", "unban", "delete", "delete_with_contributions") }
     end
 
+    context 'the user is trusted' do
+      let_it_be(:user) { create(:user, :trusted) }
+
+      it do
+        is_expected.to contain_exactly("edit",
+          "block",
+          "deactivate",
+          "ban",
+          "delete",
+          "delete_with_contributions",
+          "untrust"
+        )
+      end
+    end
+
     context 'the current_user does not have permission to delete the user' do
       let_it_be(:user) { build(:user) }
 
@@ -95,7 +140,7 @@
         allow(helper).to receive(:can?).with(current_user, :destroy_user, user).and_return(false)
       end
 
-      it { is_expected.to contain_exactly("edit", "block", "ban", "deactivate") }
+      it { is_expected.to contain_exactly("edit", "block", "ban", "deactivate", "trust") }
     end
 
     context 'the user is a sole owner of a group' do
@@ -106,7 +151,7 @@
         group.add_owner(user)
       end
 
-      it { is_expected.to contain_exactly("edit", "block", "ban", "deactivate", "delete_with_contributions") }
+      it { is_expected.to contain_exactly("edit", "block", "ban", "deactivate", "delete_with_contributions", "trust") }
     end
 
     context 'the user is a bot' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c9da1a31c8686e516dfa233bb8086dd0806535f4..09eb92e01e6bf5d67e6e31eea593b0644c48099f 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1419,6 +1419,16 @@
           'ORDER BY "users"."current_sign_in_at" ASC NULLS LAST')
       end
     end
+
+    describe '.trusted' do
+      let_it_be(:trusted_user1) { create(:user, :trusted) }
+      let_it_be(:trusted_user2) { create(:user, :trusted) }
+      let_it_be(:user3) { create(:user) }
+
+      it 'returns only the trusted users' do
+        expect(described_class.trusted).to match_array([trusted_user1, trusted_user2])
+      end
+    end
   end
 
   context 'strip attributes' do
@@ -2858,6 +2868,12 @@
 
       expect(described_class.filter_items('wop')).to include user
     end
+
+    it 'filters by trusted' do
+      expect(described_class).to receive(:trusted).and_return([user])
+
+      expect(described_class.filter_items('trusted')).to include user
+    end
   end
 
   describe '.without_projects' do