diff --git a/cmd/main.go b/cmd/main.go
index 8aa56e0..ceec3d5 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -67,12 +67,17 @@ func Serve(ctx *cli.Context) error {
 	var canonicalDomainCache = cache.NewKeyValueCache()
 	// dnsLookupCache stores DNS lookups for custom domains
 	var dnsLookupCache = cache.NewKeyValueCache()
+	// branchTimestampCache stores branch timestamps for faster cache checking
+	var branchTimestampCache = cache.NewKeyValueCache()
+	// fileResponseCache stores responses from the Gitea server
+	// TODO: make this an MRU cache with a size limit
+	var fileResponseCache = cache.NewKeyValueCache()
 
 	// Create handler based on settings
 	handler := server.Handler(mainDomainSuffix, []byte(rawDomain),
 		giteaRoot, rawInfoPage, giteaAPIToken,
 		BlacklistedPaths, allowedCorsDomains,
-		dnsLookupCache, canonicalDomainCache)
+		dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache)
 
 	fastServer := server.SetupServer(handler)
 	httpServer := server.SetupHttpACMEChallengeServer(challengeCache)
diff --git a/server/handler.go b/server/handler.go
index 89f3f4a..4b3c4ef 100644
--- a/server/handler.go
+++ b/server/handler.go
@@ -18,7 +18,7 @@ import (
 func Handler(mainDomainSuffix, rawDomain []byte,
 	giteaRoot, rawInfoPage, giteaApiToken string,
 	blacklistedPaths, allowedCorsDomains [][]byte,
-	dnsLookupCache, canonicalDomainCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) {
+	dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) {
 	return func(ctx *fasthttp.RequestCtx) {
 		log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger()
 
@@ -85,7 +85,7 @@ func Handler(mainDomainSuffix, rawDomain []byte,
 			}
 
 			// Check if the branch exists, otherwise treat it as a file path
-			branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken)
+			branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken, branchTimestampCache)
 			if branchTimestampResult == nil {
 				// branch doesn't exist
 				return false
@@ -126,7 +126,7 @@ func Handler(mainDomainSuffix, rawDomain []byte,
 			}
 
 			// Try to request the file from the Gitea API
-			if !upstream.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions) {
+			if !upstream.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions, branchTimestampCache, fileResponseCache) {
 				html.ReturnErrorPage(ctx, ctx.Response.StatusCode())
 			}
 		}
diff --git a/server/handler_test.go b/server/handler_test.go
index 3488ae7..0ec9fcd 100644
--- a/server/handler_test.go
+++ b/server/handler_test.go
@@ -20,6 +20,8 @@ func TestHandlerPerformance(t *testing.T) {
 		[][]byte{[]byte("raw.codeberg.org"), []byte("fonts.codeberg.org"), []byte("design.codeberg.org")},
 		cache.NewKeyValueCache(),
 		cache.NewKeyValueCache(),
+		cache.NewKeyValueCache(),
+		cache.NewKeyValueCache(),
 	)
 
 	ctx := &fasthttp.RequestCtx{
diff --git a/server/upstream/2rm.go b/server/upstream/2rm.go
deleted file mode 100644
index f02b452..0000000
--- a/server/upstream/2rm.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package upstream
-
-import "github.com/OrlovEvgeny/go-mcache"
-
-// branchTimestampCache stores branch timestamps for faster cache checking
-var branchTimestampCache = mcache.New()
-
-// fileResponseCache stores responses from the Gitea server
-// TODO: make this an MRU cache with a size limit
-var fileResponseCache = mcache.New()
diff --git a/server/upstream/helper.go b/server/upstream/helper.go
index eeda2c2..d8882b6 100644
--- a/server/upstream/helper.go
+++ b/server/upstream/helper.go
@@ -5,6 +5,8 @@ import (
 
 	"github.com/valyala/fasthttp"
 	"github.com/valyala/fastjson"
+
+	"codeberg.org/codeberg/pages/server/cache"
 )
 
 type branchTimestamp struct {
@@ -14,7 +16,7 @@ type branchTimestamp struct {
 
 // GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch
 // (or nil if the branch doesn't exist)
-func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string) *branchTimestamp {
+func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, branchTimestampCache cache.SetGetKey) *branchTimestamp {
 	if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok {
 		if result == nil {
 			return nil
diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go
index a269e06..3dfddb7 100644
--- a/server/upstream/upstream.go
+++ b/server/upstream/upstream.go
@@ -10,10 +10,12 @@ import (
 	"strings"
 	"time"
 
-	"codeberg.org/codeberg/pages/html"
 
 	"github.com/rs/zerolog/log"
 	"github.com/valyala/fasthttp"
+
+	"codeberg.org/codeberg/pages/html"
+	"codeberg.org/codeberg/pages/server/cache"
 )
 
 // upstreamIndexPages lists pages that may be considered as index pages for directories.
@@ -39,7 +41,7 @@ var Client = fasthttp.Client{
 }
 
 // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context.
-func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *Options) (final bool) {
+func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *Options, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) {
 	log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger()
 
 	if options.ForbiddenMimeTypes == nil {
@@ -48,7 +50,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t
 
 	// Check if the branch exists and when it was modified
 	if options.BranchTimestamp == (time.Time{}) {
-		branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken)
+		branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken, branchTimestampCache)
 
 		if branch == nil {
 			html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency)
@@ -97,7 +99,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t
 			optionsForIndexPages.TryIndexPages = false
 			optionsForIndexPages.AppendTrailingSlash = true
 			for _, indexPage := range upstreamIndexPages {
-				if Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages) {
+				if Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages, branchTimestampCache, fileResponseCache) {
 					_ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{
 						exists: false,
 					}, FileCacheTimeout)
@@ -107,7 +109,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t
 			// compatibility fix for GitHub Pages (/example → /example.html)
 			optionsForIndexPages.AppendTrailingSlash = false
 			optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html"
-			if Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages) {
+			if Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages, branchTimestampCache, fileResponseCache) {
 				_ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{
 					exists: false,
 				}, FileCacheTimeout)