diff --git a/release-notes/9.0.0/4367.md b/release-notes/9.0.0/4367.md
new file mode 100644
index 0000000000..b5528617f0
--- /dev/null
+++ b/release-notes/9.0.0/4367.md
@@ -0,0 +1 @@
+The caching of contributor stats was improved (the data used by `/<user>/<repo>/activity/recent-commits`) to use the configured cache TTL from the config (`[cache].ITEM_TTL`) instead of a hardcoded TTL of ten minutes. The computation of this operation is computationally heavy and makes a lot of requests to the database and Git on repositories with a lot of commits. It should be cached for longer than what was previously hardcoded, ten minutes.
diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go
index f26a87e6ac..6b35c82726 100644
--- a/services/repository/contributors_graph.go
+++ b/services/repository/contributors_graph.go
@@ -22,15 +22,13 @@ import (
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"gitea.com/go-chi/cache"
 )
 
-const (
-	contributorStatsCacheKey           = "GetContributorStats/%s/%s"
-	contributorStatsCacheTimeout int64 = 60 * 10
-)
+const contributorStatsCacheKey = "GetContributorStats/%s/%s"
 
 var (
 	ErrAwaitGeneration  = errors.New("generation took longer than ")
@@ -211,8 +209,7 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
 
 	gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
 	if err != nil {
-		err := fmt.Errorf("OpenRepository: %w", err)
-		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		log.Error("OpenRepository[repo=%q]: %v", repo.FullName(), err)
 		return
 	}
 	defer closer.Close()
@@ -222,13 +219,11 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
 	}
 	extendedCommitStats, err := getExtendedCommitStats(gitRepo, revision)
 	if err != nil {
-		err := fmt.Errorf("ExtendedCommitStats: %w", err)
-		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		log.Error("getExtendedCommitStats[repo=%q revision=%q]: %v", repo.FullName(), revision, err)
 		return
 	}
 	if len(extendedCommitStats) == 0 {
-		err := fmt.Errorf("no commit stats returned for revision '%s'", revision)
-		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		log.Error("No commit stats were returned [repo=%q revision=%q]", repo.FullName(), revision)
 		return
 	}
 
@@ -312,14 +307,13 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
 
 	data, err := json.Marshal(contributorsCommitStats)
 	if err != nil {
-		err := fmt.Errorf("couldn't marshal the data: %w", err)
-		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		log.Error("json.Marshal[repo=%q revision=%q]: %v", repo.FullName(), revision, err)
 		return
 	}
 
 	// Store the data as an string, to make it uniform what data type is returned
 	// from caches.
-	_ = cache.Put(cacheKey, string(data), contributorStatsCacheTimeout)
+	_ = cache.Put(cacheKey, string(data), setting.CacheService.TTLSeconds())
 	generateLock.Delete(cacheKey)
 	if genDone != nil {
 		genDone <- struct{}{}
diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go
index 2c6102005d..a04587e243 100644
--- a/services/repository/contributors_graph_test.go
+++ b/services/repository/contributors_graph_test.go
@@ -6,12 +6,14 @@ package repository
 import (
 	"slices"
 	"testing"
+	"time"
 
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
-	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/test"
 
 	"gitea.com/go-chi/cache"
 	"github.com/stretchr/testify/assert"
@@ -27,10 +29,14 @@ func TestRepository_ContributorsGraph(t *testing.T) {
 	})
 	assert.NoError(t, err)
 
+	lc, cleanup := test.NewLogChecker(log.DEFAULT, log.INFO)
+	lc.StopMark(`getExtendedCommitStats[repo="user2/repo2" revision="404ref"]: object does not exist [id: 404ref, rel_path: ]`)
+	defer cleanup()
+
 	generateContributorStats(nil, mockCache, "key", repo, "404ref")
-	err, isErr := mockCache.Get("key").(error)
-	assert.True(t, isErr)
-	assert.ErrorAs(t, err, &git.ErrNotExist{})
+	assert.False(t, mockCache.IsExist("key"))
+	_, stopped := lc.Check(100 * time.Millisecond)
+	assert.True(t, stopped)
 
 	generateContributorStats(nil, mockCache, "key2", repo, "master")
 	dataString, isData := mockCache.Get("key2").(string)