diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 3876396a31..108247419b 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -659,6 +659,8 @@ EMAIL_DOMAIN_WHITELIST =
 EMAIL_DOMAIN_BLOCKLIST =
 ; Disallow registration, only allow admins to create accounts.
 DISABLE_REGISTRATION = false
+; Allow registration only using gitea itself, it works only when DISABLE_REGISTRATION is false
+ALLOW_ONLY_INTERNAL_REGISTRATION = false
 ; Allow registration only using third-party services, it works only when DISABLE_REGISTRATION is false
 ALLOW_ONLY_EXTERNAL_REGISTRATION = false
 ; User must sign in to view anything.
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 0a2647768b..eb169035ee 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -497,6 +497,7 @@ relation to port exhaustion.
 - `AUTO_WATCH_ON_CHANGES`: **false**: Enable this to make users watch a repository after their first commit to it
 - `DEFAULT_ORG_VISIBILITY`: **public**: Set default visibility mode for organisations, either "public", "limited" or "private".
 - `DEFAULT_ORG_MEMBER_VISIBLE`: **false** True will make the membership of the users visible when added to the organisation.
+- `ALLOW_ONLY_INTERNAL_REGISTRATION`: **false** Set to true to force registration only via gitea.
 - `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
 - `NO_REPLY_ADDRESS`: **noreply.DOMAIN** Value for the domain part of the user's email address in the git log if user has set KeepEmailPrivate to true. DOMAIN resolves to the value in server.DOMAIN.
   The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
diff --git a/modules/setting/service.go b/modules/setting/service.go
index 9696e98641..41e834e8e6 100644
--- a/modules/setting/service.go
+++ b/modules/setting/service.go
@@ -23,6 +23,7 @@ var Service struct {
 	EmailDomainWhitelist                    []string
 	EmailDomainBlocklist                    []string
 	DisableRegistration                     bool
+	AllowOnlyInternalRegistration           bool
 	AllowOnlyExternalRegistration           bool
 	ShowRegistrationButton                  bool
 	ShowMilestonesDashboardPage             bool
@@ -73,7 +74,12 @@ func newService() {
 	Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
 	Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
 	Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
+	Service.AllowOnlyInternalRegistration = sec.Key("ALLOW_ONLY_INTERNAL_REGISTRATION").MustBool()
 	Service.AllowOnlyExternalRegistration = sec.Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").MustBool()
+	if Service.AllowOnlyExternalRegistration && Service.AllowOnlyInternalRegistration {
+		log.Warn("ALLOW_ONLY_INTERNAL_REGISTRATION and ALLOW_ONLY_EXTERNAL_REGISTRATION are true - disabling registration")
+		Service.DisableRegistration = true
+	}
 	if !sec.Key("REGISTER_EMAIL_CONFIRM").MustBool() {
 		Service.RegisterManualConfirm = sec.Key("REGISTER_MANUAL_CONFIRM").MustBool(false)
 	} else {
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 51fa316022..7e9229e1cd 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -2412,6 +2412,7 @@ config.db_path = Path
 config.service_config = Service Configuration
 config.register_email_confirm = Require Email Confirmation to Register
 config.disable_register = Disable Self-Registration
+config.allow_only_internal_registration = Allow Registration Only Through Gitea itself
 config.allow_only_external_registration = Allow Registration Only Through External Services
 config.enable_openid_signup = Enable OpenID Self-Registration
 config.enable_openid_signin = Enable OpenID Sign-In
diff --git a/routers/user/auth.go b/routers/user/auth.go
index f29e1cc4d0..cfe116c902 100644
--- a/routers/user/auth.go
+++ b/routers/user/auth.go
@@ -617,7 +617,7 @@ func SignInOAuthCallback(ctx *context.Context) {
 	}
 
 	if u == nil {
-		if setting.OAuth2Client.EnableAutoRegistration {
+		if !(setting.Service.DisableRegistration || setting.Service.AllowOnlyInternalRegistration) && setting.OAuth2Client.EnableAutoRegistration {
 			// create new user with details from oauth2 provider
 			var missingFields []string
 			if gothUser.UserID == "" {
@@ -828,6 +828,7 @@ func LinkAccount(ctx *context.Context) {
 	ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
 	ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
 	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
+	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
 	ctx.Data["ShowRegistrationButton"] = false
 
 	// use this to set the right link into the signIn and signUp templates in the link_account template
@@ -993,7 +994,7 @@ func LinkAccountPostRegister(ctx *context.Context) {
 		return
 	}
 
-	if setting.Service.DisableRegistration {
+	if setting.Service.DisableRegistration || setting.Service.AllowOnlyInternalRegistration {
 		ctx.Error(http.StatusForbidden)
 		return
 	}
diff --git a/routers/user/auth_openid.go b/routers/user/auth_openid.go
index 863fa67184..45405320e2 100644
--- a/routers/user/auth_openid.go
+++ b/routers/user/auth_openid.go
@@ -249,7 +249,7 @@ func signInOpenIDVerify(ctx *context.Context) {
 		log.Error("signInOpenIDVerify: Unable to save changes to the session: %v", err)
 	}
 
-	if u != nil || !setting.Service.EnableOpenIDSignUp {
+	if u != nil || !setting.Service.EnableOpenIDSignUp || setting.Service.AllowOnlyInternalRegistration {
 		ctx.Redirect(setting.AppSubURL + "/user/openid/connect")
 	} else {
 		ctx.Redirect(setting.AppSubURL + "/user/openid/register")
@@ -267,6 +267,7 @@ func ConnectOpenID(ctx *context.Context) {
 	ctx.Data["PageIsSignIn"] = true
 	ctx.Data["PageIsOpenIDConnect"] = true
 	ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
+	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
 	ctx.Data["OpenID"] = oid
 	userName, _ := ctx.Session.Get("openid_determined_username").(string)
 	if userName != "" {
@@ -328,6 +329,7 @@ func RegisterOpenID(ctx *context.Context) {
 	ctx.Data["PageIsSignIn"] = true
 	ctx.Data["PageIsOpenIDRegister"] = true
 	ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
+	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
 	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
 	ctx.Data["Captcha"] = context.GetImageCaptcha()
 	ctx.Data["CaptchaType"] = setting.Service.CaptchaType
@@ -367,6 +369,11 @@ func RegisterOpenIDPost(ctx *context.Context) {
 	ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
 	ctx.Data["OpenID"] = oid
 
+	if setting.Service.AllowOnlyInternalRegistration {
+		ctx.Error(http.StatusForbidden)
+		return
+	}
+
 	if setting.Service.EnableCaptcha {
 		var valid bool
 		var err error
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 6979512df7..b419d04a1b 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -149,6 +149,8 @@
 				<dd>{{if .Service.RegisterEmailConfirm}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 				<dt>{{.i18n.Tr "admin.config.disable_register"}}</dt>
 				<dd>{{if .Service.DisableRegistration}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
+				<dt>{{.i18n.Tr "admin.config.allow_only_internal_registration"}}</dt>
+				<dd>{{if .Service.AllowOnlyInternalRegistration}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 				<dt>{{.i18n.Tr "admin.config.allow_only_external_registration"}}</dt>
 				<dd>{{if .Service.AllowOnlyExternalRegistration}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 				<dt>{{.i18n.Tr "admin.config.show_registration_button"}}</dt>
diff --git a/templates/user/auth/link_account.tmpl b/templates/user/auth/link_account.tmpl
index 39e312bd66..dfc70b1ae6 100644
--- a/templates/user/auth/link_account.tmpl
+++ b/templates/user/auth/link_account.tmpl
@@ -3,10 +3,12 @@
 	<div class="ui secondary pointing tabular top attached borderless menu new-menu navbar">
 		<div class="new-menu-inner">
 			<!-- TODO handle .ShowRegistrationButton once other login bugs are fixed -->
-			<a class="item {{if not .user_exists}}active{{end}}"
-				data-tab="auth-link-signup-tab">
-				{{.i18n.Tr "auth.oauth_signup_tab"}}
-			</a>
+			{{if not .AllowOnlyInternalRegistration}}
+				<a class="item {{if not .user_exists}}active{{end}}"
+					data-tab="auth-link-signup-tab">
+					{{.i18n.Tr "auth.oauth_signup_tab"}}
+				</a>
+			{{end}}
 			<a class="item {{if .user_exists}}active{{end}}"
 				data-tab="auth-link-signin-tab">
 				{{.i18n.Tr "auth.oauth_signin_tab"}}
diff --git a/templates/user/auth/signup_openid_navbar.tmpl b/templates/user/auth/signup_openid_navbar.tmpl
index 9928bb6198..b033022a8e 100644
--- a/templates/user/auth/signup_openid_navbar.tmpl
+++ b/templates/user/auth/signup_openid_navbar.tmpl
@@ -3,7 +3,7 @@
 		<a class="{{if .PageIsOpenIDConnect}}active{{end}} item" href="{{AppSubUrl}}/user/openid/connect">
 			{{.i18n.Tr "auth.openid_connect_title"}}
 		</a>
-		{{if .EnableOpenIDSignUp}}
+		{{if and .EnableOpenIDSignUp (not .AllowOnlyInternalRegistration)}}
 			<a class="{{if .PageIsOpenIDRegister}}active{{end}} item" href="{{AppSubUrl}}/user/openid/register">
 				{{.i18n.Tr "auth.openid_register_title"}}
 			</a>