diff --git a/integrations/goget_test.go b/integrations/goget_test.go
new file mode 100644
index 000000000..1003d7102
--- /dev/null
+++ b/integrations/goget_test.go
@@ -0,0 +1,35 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"code.gitea.io/gitea/modules/setting"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGoGet(t *testing.T) {
+	defer prepareTestEnv(t)()
+
+	req := NewRequest(t, "GET", "/blah/glah/plah?go-get=1")
+	resp := MakeRequest(t, req, http.StatusOK)
+
+	expected := fmt.Sprintf(`<!doctype html>
+<html>
+	<head>
+		<meta name="go-import" content="%[1]s:%[2]s/blah/glah git %[3]sblah/glah.git">
+		<meta name="go-source" content="%[1]s:%[2]s/blah/glah _ %[3]sblah/glah/src/branch/master{/dir} %[3]sblah/glah/src/branch/master{/dir}/{file}#L{line}">
+	</head>
+	<body>
+		go get --insecure %[1]s:%[2]s/blah/glah
+	</body>
+</html>
+`, setting.Domain, setting.HTTPPort, setting.AppURL)
+
+	assert.Equal(t, expected, resp.Body.String())
+}
diff --git a/routers/routes/goget.go b/routers/routes/goget.go
new file mode 100644
index 000000000..518f5e307
--- /dev/null
+++ b/routers/routes/goget.go
@@ -0,0 +1,86 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package routes
+
+import (
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/util"
+	"github.com/unknwon/com"
+)
+
+func goGet(ctx *context.Context) {
+	if ctx.Req.Method != "GET" || ctx.Query("go-get") != "1" || len(ctx.Req.URL.Query()) > 1 {
+		return
+	}
+
+	parts := strings.SplitN(ctx.Req.URL.EscapedPath(), "/", 4)
+
+	if len(parts) < 3 {
+		return
+	}
+
+	ownerName := parts[1]
+	repoName := parts[2]
+
+	// Quick responses appropriate go-get meta with status 200
+	// regardless of if user have access to the repository,
+	// or the repository does not exist at all.
+	// This is particular a workaround for "go get" command which does not respect
+	// .netrc file.
+
+	trimmedRepoName := strings.TrimSuffix(repoName, ".git")
+
+	if ownerName == "" || trimmedRepoName == "" {
+		_, _ = ctx.Write([]byte(`<!doctype html>
+<html>
+	<body>
+		invalid import path
+	</body>
+</html>
+`))
+		ctx.Status(400)
+		return
+	}
+	branchName := setting.Repository.DefaultBranch
+
+	repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
+	if err == nil && len(repo.DefaultBranch) > 0 {
+		branchName = repo.DefaultBranch
+	}
+	prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
+
+	appURL, _ := url.Parse(setting.AppURL)
+
+	insecure := ""
+	if appURL.Scheme == string(setting.HTTP) {
+		insecure = "--insecure "
+	}
+	ctx.Header().Set("Content-Type", "text/html")
+	ctx.Status(http.StatusOK)
+	_, _ = ctx.Write([]byte(com.Expand(`<!doctype html>
+<html>
+	<head>
+		<meta name="go-import" content="{GoGetImport} git {CloneLink}">
+		<meta name="go-source" content="{GoGetImport} _ {GoDocDirectory} {GoDocFile}">
+	</head>
+	<body>
+		go get {Insecure}{GoGetImport}
+	</body>
+</html>
+`, map[string]string{
+		"GoGetImport":    context.ComposeGoGetImport(ownerName, trimmedRepoName),
+		"CloneLink":      models.ComposeHTTPSCloneURL(ownerName, repoName),
+		"GoDocDirectory": prefix + "{/dir}",
+		"GoDocFile":      prefix + "{/dir}/{file}#L{line}",
+		"Insecure":       insecure,
+	})))
+}
diff --git a/routers/routes/web.go b/routers/routes/web.go
index 77ceb8321..008c745d6 100644
--- a/routers/routes/web.go
+++ b/routers/routes/web.go
@@ -8,7 +8,6 @@ import (
 	"encoding/gob"
 	"fmt"
 	"net/http"
-	"net/url"
 	"os"
 	"path"
 	"strings"
@@ -22,7 +21,6 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/storage"
 	"code.gitea.io/gitea/modules/templates"
-	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/validation"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/routers"
@@ -51,7 +49,6 @@ import (
 	"github.com/go-chi/cors"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/tstranex/u2f"
-	"github.com/unknwon/com"
 )
 
 const (
@@ -228,6 +225,7 @@ func WebRoutes() *web.Route {
 	// TODO: These really seem like things that could be folded into Contexter or as helper functions
 	common = append(common, user.GetNotificationCount)
 	common = append(common, repo.GetActiveStopwatch)
+	common = append(common, goGet)
 
 	others := web.NewRoute()
 	for _, middle := range common {
@@ -239,67 +237,6 @@ func WebRoutes() *web.Route {
 	return routes
 }
 
-func goGet(ctx *context.Context) {
-	if ctx.Query("go-get") != "1" {
-		return
-	}
-
-	// Quick responses appropriate go-get meta with status 200
-	// regardless of if user have access to the repository,
-	// or the repository does not exist at all.
-	// This is particular a workaround for "go get" command which does not respect
-	// .netrc file.
-
-	ownerName := ctx.Params(":username")
-	repoName := ctx.Params(":reponame")
-	trimmedRepoName := strings.TrimSuffix(repoName, ".git")
-
-	if ownerName == "" || trimmedRepoName == "" {
-		_, _ = ctx.Write([]byte(`<!doctype html>
-<html>
-	<body>
-		invalid import path
-	</body>
-</html>
-`))
-		ctx.Status(400)
-		return
-	}
-	branchName := setting.Repository.DefaultBranch
-
-	repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
-	if err == nil && len(repo.DefaultBranch) > 0 {
-		branchName = repo.DefaultBranch
-	}
-	prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
-
-	appURL, _ := url.Parse(setting.AppURL)
-
-	insecure := ""
-	if appURL.Scheme == string(setting.HTTP) {
-		insecure = "--insecure "
-	}
-	ctx.Header().Set("Content-Type", "text/html")
-	ctx.Status(http.StatusOK)
-	_, _ = ctx.Write([]byte(com.Expand(`<!doctype html>
-<html>
-	<head>
-		<meta name="go-import" content="{GoGetImport} git {CloneLink}">
-		<meta name="go-source" content="{GoGetImport} _ {GoDocDirectory} {GoDocFile}">
-	</head>
-	<body>
-		go get {Insecure}{GoGetImport}
-	</body>
-</html>
-`, map[string]string{
-		"GoGetImport":    context.ComposeGoGetImport(ownerName, trimmedRepoName),
-		"CloneLink":      models.ComposeHTTPSCloneURL(ownerName, repoName),
-		"GoDocDirectory": prefix + "{/dir}",
-		"GoDocFile":      prefix + "{/dir}/{file}#L{line}",
-		"Insecure":       insecure,
-	})))
-}
-
 // RegisterRoutes register routes
 func RegisterRoutes(m *web.Route) {
 	reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})
@@ -1104,7 +1041,7 @@ func RegisterRoutes(m *web.Route) {
 	m.Group("/{username}", func() {
 		m.Group("/{reponame}", func() {
 			m.Get("", repo.SetEditorconfigIfExists, repo.Home)
-		}, goGet, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
+		}, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
 
 		m.Group("/{reponame}", func() {
 			m.Group("/info/lfs", func() {