diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index ec0b7c5235..b59ceee4f1 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2196,7 +2196,8 @@ ROUTER = console
 ;SHOW_FOOTER_VERSION = true
 ;; Show template execution time in the footer
 ;SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
-
+;; Generate sitemap. Defaults to `true`.
+; ENABLE_SITEMAP = true
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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 ebc860c457..df1911934c 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -1233,3 +1233,4 @@ PROXY_HOSTS = *.github.com
 - `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer.
 - `SHOW_FOOTER_VERSION`: **true**: Show Gitea and Go version information in the footer.
 - `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer.
+- `ENABLE_SITEMAP`: **true**: Generate sitemap.
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 9c4f4ced12..043acb733d 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -452,6 +452,7 @@ var (
 	RunUser       string
 	IsWindows     bool
 	HasRobotsTxt  bool
+	EnableSitemap bool
 	InternalToken string // internal access token
 )
 
@@ -1100,6 +1101,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
 	ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false)
 	ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool(true)
 	ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true)
+	EnableSitemap = Cfg.Section("other").Key("ENABLE_SITEMAP").MustBool(true)
 
 	UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
 	UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
diff --git a/routers/web/web.go b/routers/web/web.go
index 9b814c3f54..0b16e756e1 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -296,12 +296,19 @@ func RegisterRoutes(m *web.Route) {
 		}
 	}
 
+	sitemapEnabled := func(ctx *context.Context) {
+		if !setting.EnableSitemap {
+			ctx.Error(http.StatusNotFound)
+			return
+		}
+	}
+
 	// FIXME: not all routes need go through same middleware.
 	// Especially some AJAX requests, we can reduce middleware number to improve performance.
 	// Routers.
 	// for health check
 	m.Get("/", Home)
-	m.Get("/sitemap.xml", ignExploreSignIn, HomeSitemap)
+	m.Get("/sitemap.xml", sitemapEnabled, ignExploreSignIn, HomeSitemap)
 	m.Group("/.well-known", func() {
 		m.Get("/openid-configuration", auth.OIDCWellKnown)
 		m.Group("", func() {
@@ -318,9 +325,9 @@ func RegisterRoutes(m *web.Route) {
 			ctx.Redirect(setting.AppSubURL + "/explore/repos")
 		})
 		m.Get("/repos", explore.Repos)
-		m.Get("/repos/sitemap-{idx}.xml", explore.Repos)
+		m.Get("/repos/sitemap-{idx}.xml", sitemapEnabled, explore.Repos)
 		m.Get("/users", explore.Users)
-		m.Get("/users/sitemap-{idx}.xml", explore.Users)
+		m.Get("/users/sitemap-{idx}.xml", sitemapEnabled, explore.Users)
 		m.Get("/organizations", explore.Organizations)
 		m.Get("/code", explore.Code)
 		m.Get("/topics/search", explore.TopicSearch)