diff --git a/models/repo.go b/models/repo.go
index 8b51f14043..f180179440 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1077,9 +1077,11 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
 		}
 	}
 
-	if err := cleanUpMigrateGitConfig(repo.GitConfigPath()); err != nil {
-		return repo, fmt.Errorf("cleanUpMigrateGitConfig: %v", err)
+	_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
+	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+		return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
 	}
+
 	if repo.HasWiki() {
 		if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil {
 			return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err)
diff --git a/models/repo_mirror.go b/models/repo_mirror.go
index 759241e461..b63fba5741 100644
--- a/models/repo_mirror.go
+++ b/models/repo_mirror.go
@@ -20,7 +20,6 @@ import (
 
 	"github.com/Unknwon/com"
 	"github.com/go-xorm/xorm"
-	"gopkg.in/ini.v1"
 )
 
 // MirrorQueue holds an UniqueQueue object of the mirror
@@ -71,11 +70,18 @@ func (m *Mirror) ScheduleNextUpdate() {
 }
 
 func remoteAddress(repoPath string) (string, error) {
-	cfg, err := ini.Load(GitConfigPath(repoPath))
+	cmd := git.NewCommand("remote", "get-url", "origin")
+	result, err := cmd.RunInDir(repoPath)
 	if err != nil {
+		if strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+			return "", nil
+		}
 		return "", err
 	}
-	return cfg.Section("remote \"origin\"").Key("url").Value(), nil
+	if len(result) > 0 {
+		return result[:len(result)-1], nil
+	}
+	return "", nil
 }
 
 func (m *Mirror) readAddress() {
@@ -115,14 +121,15 @@ func (m *Mirror) FullAddress() string {
 
 // SaveAddress writes new address to Git repository config.
 func (m *Mirror) SaveAddress(addr string) error {
-	configPath := m.Repo.GitConfigPath()
-	cfg, err := ini.Load(configPath)
-	if err != nil {
-		return fmt.Errorf("Load: %v", err)
+	repoPath := m.Repo.RepoPath()
+	// Remove old origin
+	_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
+	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+		return err
 	}
 
-	cfg.Section("remote \"origin\"").Key("url").SetValue(addr)
-	return cfg.SaveToIndent(configPath, "\t")
+	_, err = git.NewCommand("remote", "add", "origin", addr).RunInDir(repoPath)
+	return err
 }
 
 // gitShortEmptySha Git short empty SHA
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index ce726ed562..23d1949203 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -567,7 +567,9 @@ mirror_prune_desc = Remove obsolete remote-tracking references
 mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable automatic sync.
 mirror_interval_invalid = The mirror interval is not valid.
 mirror_address = Clone From URL
-mirror_address_desc = Include any required authorization credentials in the URL.
+mirror_address_desc = Include any required authorization credentials in the URL. These must be url escaped as appropriate
+mirror_address_url_invalid = The provided url is invalid. You must escape all components of the url correctly.
+mirror_address_protocol_invalid = The provided url is invalid. Only http(s):// or git:// locations can be mirrored from.
 mirror_last_synced = Last Synchronized
 watchers = Watchers
 stargazers = Stargazers
diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index 0101b2362b..f58601633a 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -7,9 +7,13 @@ package repo
 
 import (
 	"errors"
+	"net/url"
+	"regexp"
 	"strings"
 	"time"
 
+	"mvdan.cc/xurls/v2"
+
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/auth"
 	"code.gitea.io/gitea/modules/base"
@@ -32,6 +36,8 @@ const (
 	tplProtectedBranch base.TplName = "repo/settings/protected_branch"
 )
 
+var validFormAddress *regexp.Regexp
+
 // Settings show a repository's settings page
 func Settings(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
@@ -145,7 +151,38 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
 				return
 			}
 		}
-		if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil {
+
+		// Validate the form.MirrorAddress
+		u, err := url.Parse(form.MirrorAddress)
+		if err != nil {
+			ctx.Data["Err_MirrorAddress"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form)
+			return
+		}
+
+		if u.Opaque != "" || !(u.Scheme == "http" || u.Scheme == "https" || u.Scheme == "git") {
+			ctx.Data["Err_MirrorAddress"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tplSettingsOptions, &form)
+			return
+		}
+
+		// Now use xurls
+		address := validFormAddress.FindString(form.MirrorAddress)
+		if address != form.MirrorAddress && form.MirrorAddress != "" {
+			ctx.Data["Err_MirrorAddress"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form)
+			return
+		}
+
+		if u.EscapedPath() == "" || u.Host == "" || !u.IsAbs() {
+			ctx.Data["Err_MirrorAddress"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form)
+			return
+		}
+
+		address = u.String()
+
+		if err := ctx.Repo.Mirror.SaveAddress(address); err != nil {
 			ctx.ServerError("SaveAddress", err)
 			return
 		}
@@ -682,3 +719,11 @@ func DeleteDeployKey(ctx *context.Context) {
 		"redirect": ctx.Repo.RepoLink + "/settings/keys",
 	})
 }
+
+func init() {
+	var err error
+	validFormAddress, err = xurls.StrictMatchingScheme(`(https?)|(git)://`)
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 7e25206c9d..25120fcb9f 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -62,7 +62,7 @@
 						<label for="interval">{{.i18n.Tr "repo.mirror_interval"}}</label>
 						<input id="interval" name="interval" value="{{.MirrorInterval}}">
 					</div>
-					<div class="field">
+					<div class="field {{if .Err_MirrorAddress}}error{{end}}">
 						<label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label>
 						<input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required>
 						<p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p>