diff --git a/internal/upload/accelerate.go b/internal/upload/accelerate.go
index 7c2e635d64d43c060cffc11a9fd05421828959dd..b8e5f6ba443136051e1b692b612e40b731a9b312 100644
--- a/internal/upload/accelerate.go
+++ b/internal/upload/accelerate.go
@@ -25,13 +25,15 @@ type MultipartClaims struct {
 	jwt.StandardClaims
 }
 
-func Accelerate(tempDir string, h http.Handler) http.Handler {
-	// TODO: for Object Store this will need a authorize call
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		localOnlyPreAuth := &api.Response{TempPath: tempDir}
+type PreAuthorizer interface {
+	PreAuthorizeHandler(next api.HandleFunc, suffix string) http.Handler
+}
+
+func Accelerate(rails PreAuthorizer, h http.Handler) http.Handler {
+	return rails.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
 		s := &savedFileTracker{request: r}
-		HandleFileUploads(w, r, h, localOnlyPreAuth, s)
-	})
+		HandleFileUploads(w, r, h, a, s)
+	}, "/authorize")
 }
 
 func (s *savedFileTracker) ProcessFile(_ context.Context, fieldName string, file *filestore.FileHandler, _ *multipart.Writer) error {
diff --git a/internal/upload/skip_rails_authorizer.go b/internal/upload/skip_rails_authorizer.go
new file mode 100644
index 0000000000000000000000000000000000000000..716467b884174b04ead1ccf1e404f6abf15c5c29
--- /dev/null
+++ b/internal/upload/skip_rails_authorizer.go
@@ -0,0 +1,22 @@
+package upload
+
+import (
+	"net/http"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+)
+
+// SkipRailsAuthorizer implements a fake PreAuthorizer that do not calls rails API and
+// authorize each call as a local only upload to TempPath
+type SkipRailsAuthorizer struct {
+	// TempPath is the temporary path for a local only upload
+	TempPath string
+}
+
+// PreAuthorizeHandler implements PreAuthorizer. It always grant the upload.
+// The fake API response contains only TempPath
+func (l *SkipRailsAuthorizer) PreAuthorizeHandler(next api.HandleFunc, _ string) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		next(w, r, &api.Response{TempPath: l.TempPath})
+	})
+}
diff --git a/internal/upstream/routes.go b/internal/upstream/routes.go
index 4e5258421b4bf4372c0500a4ae9d22b7c8ffac3c..328418a1d140a22abd49a14fec39b285e430c7b3 100644
--- a/internal/upstream/routes.go
+++ b/internal/upstream/routes.go
@@ -145,7 +145,8 @@ func (u *Upstream) configureRoutes() {
 		sendurl.SendURL,
 	)
 
-	uploadAccelerateProxy := upload.Accelerate(path.Join(u.DocumentRoot, "uploads/tmp"), proxy)
+	uploadPath := path.Join(u.DocumentRoot, "uploads/tmp")
+	uploadAccelerateProxy := upload.Accelerate(&upload.SkipRailsAuthorizer{TempPath: uploadPath}, proxy)
 	ciAPIProxyQueue := queueing.QueueRequests("ci_api_job_requests", uploadAccelerateProxy, u.APILimit, u.APIQueueLimit, u.APIQueueTimeout)
 	ciAPILongPolling := builds.RegisterHandler(ciAPIProxyQueue, redis.WatchKey, u.APICILongPollingDuration)
 
@@ -182,6 +183,9 @@ func (u *Upstream) configureRoutes() {
 			),
 		),
 
+		// Uploads
+		route("POST", projectPattern+`uploads\z`, upload.Accelerate(api, proxy)),
+
 		// For legacy reasons, user uploads are stored under the document root.
 		// To prevent anybody who knows/guesses the URL of a user-uploaded file
 		// from downloading it we make sure requests to /uploads/ do _not_ pass