diff --git a/modules/context/api.go b/modules/context/api.go
index b079385aff..432d285a98 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -288,6 +288,7 @@ func APIContexter() func(http.Handler) http.Handler {
 				},
 				Org: &APIOrganization{},
 			}
+			defer ctx.Close()
 
 			ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
 			ctx.csrf = Csrfer(csrfOpts, ctx.Context)
diff --git a/modules/context/context.go b/modules/context/context.go
index 7fae0b110f..381a914786 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -71,6 +71,16 @@ type Context struct {
 	Org  *Organization
 }
 
+// Close frees all resources hold by Context
+func (ctx *Context) Close() error {
+	var err error
+	if ctx.Req != nil && ctx.Req.MultipartForm != nil {
+		err = ctx.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
+	}
+	// TODO: close opened repo, and more
+	return err
+}
+
 // TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString.
 // This is useful if the locale message is intended to only produce HTML content.
 func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
@@ -643,6 +653,8 @@ func Contexter() func(next http.Handler) http.Handler {
 					"RunModeIsProd": setting.IsProd,
 				},
 			}
+			defer ctx.Close()
+
 			// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
 			ctx.PageData = map[string]interface{}{}
 			ctx.Data["PageData"] = ctx.PageData
diff --git a/modules/context/private.go b/modules/context/private.go
index dfefa1d2f0..05d60d0403 100644
--- a/modules/context/private.go
+++ b/modules/context/private.go
@@ -38,6 +38,8 @@ func PrivateContexter() func(http.Handler) http.Handler {
 					Data: map[string]interface{}{},
 				},
 			}
+			defer ctx.Close()
+
 			ctx.Req = WithPrivateContext(req, ctx)
 			next.ServeHTTP(ctx.Resp, ctx.Req)
 		})
diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go
index 62ec21f6fe..0009468690 100644
--- a/modules/test/context_tests.go
+++ b/modules/test/context_tests.go
@@ -39,6 +39,7 @@ func MockContext(t *testing.T, path string) *context.Context {
 		Resp:   context.NewResponse(resp),
 		Locale: &mockLocale{},
 	}
+	defer ctx.Close()
 
 	requestURL, err := url.Parse(path)
 	assert.NoError(t, err)
diff --git a/routers/api/v1/misc/markdown_test.go b/routers/api/v1/misc/markdown_test.go
index dc6762c4cf..6248e11754 100644
--- a/routers/api/v1/misc/markdown_test.go
+++ b/routers/api/v1/misc/markdown_test.go
@@ -35,6 +35,8 @@ func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecor
 		Render: rnd,
 		Data:   make(map[string]interface{}),
 	}
+	defer c.Close()
+
 	return c, resp
 }
 
diff --git a/routers/install/install.go b/routers/install/install.go
index 1633b174ec..a31a9bafb0 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -84,6 +84,8 @@ func Init(next http.Handler) http.Handler {
 				"PasswordHashAlgorithms": user_model.AvailableHashAlgorithms,
 			},
 		}
+		defer ctx.Close()
+
 		for _, lang := range translation.AllLangs() {
 			if lang.Lang == locale.Language() {
 				ctx.Data["LangName"] = lang.Name