diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index bd7631c7e78dbd23b64f48abbeb1d95035219c51..79c4f9a02601a658f05b0266af2108af750e6d7c 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -13,6 +13,9 @@ class RegistrationsController < Devise::RegistrationsController
   before_action :ensure_destroy_prerequisites_met, only: [:destroy]
   before_action :load_recaptcha, only: :new
   before_action :set_invite_params, only: :new
+  before_action only: [:create] do
+    check_rate_limit!(:user_sign_up, scope: request.ip) if Feature.enabled?(:rate_limit_user_sign_up_endpoint, default_enabled: :yaml)
+  end
 
   feature_category :authentication_and_authorization
 
diff --git a/config/feature_flags/development/rate_limit_user_sign_up_endpoint.yml b/config/feature_flags/development/rate_limit_user_sign_up_endpoint.yml
new file mode 100644
index 0000000000000000000000000000000000000000..af1957e54c8704f6fb5e6359c94223d539357a13
--- /dev/null
+++ b/config/feature_flags/development/rate_limit_user_sign_up_endpoint.yml
@@ -0,0 +1,8 @@
+---
+name: rate_limit_user_sign_up_endpoint
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77835
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349843
+milestone: '14.7'
+type: development
+group: group::optimize
+default_enabled: false
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index dac1f686da7062c6012c9f6ead439b861bafa166..b90f1f4da0dd333b493fc65f69fce2367525e595 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -51,6 +51,7 @@ def rate_limits
           web_hook_calls:               { interval: 1.minute },
           users_get_by_id:              { threshold: 10, interval: 1.minute },
           username_exists:              { threshold: 20, interval: 1.minute },
+          user_sign_up:                 { threshold: 20, interval: 1.minute },
           profile_resend_email_confirmation:  { threshold: 5, interval: 1.minute },
           profile_update_username:            { threshold: 10, interval: 1.minute },
           update_environment_canary_ingress:  { threshold: 1, interval: 1.minute },
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 889401e78f8715b7e4f1d7de3749cbac43ad3e05..d5fe32ac09434f765d51d1078cf96c027dd30b4b 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -20,6 +20,10 @@
   end
 
   describe '#create' do
+    before do
+      allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(false)
+    end
+
     let_it_be(:base_user_params) do
       { first_name: 'first', last_name: 'last', username: 'new_username', email: 'new@user.com', password: 'Any_password' }
     end
@@ -410,6 +414,18 @@
       end
     end
 
+    context 'when the rate limit has been reached' do
+      it 'returns status 429 Too Many Requests', :aggregate_failures do
+        ip = '1.2.3.4'
+        expect(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(:user_sign_up, scope: ip).and_return(true)
+
+        controller.request.env['REMOTE_ADDR'] = ip
+        post(:create, params: user_params, session: session_params)
+
+        expect(response).to have_gitlab_http_status(:too_many_requests)
+      end
+    end
+
     it "logs a 'User Created' message" do
       expect(Gitlab::AppLogger).to receive(:info).with(/\AUser Created: username=new_username email=new@user.com.+\z/).and_call_original