# How to Handle File Uploads with Size Limits in Gin

> Learn how to handle file uploads with size limits in Gin. Configure MaxMultipartMemory and check ContentLength to reject oversized uploads efficiently.

- Repository: [Gin-Gonic/gin](https://github.com/gin-gonic/gin)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Set `Engine.MaxMultipartMemory` to define the memory buffer for parsing multipart forms, and optionally check `c.Request.ContentLength` to reject oversized uploads before processing begins.**

Gin processes multipart/form-data requests using the Go standard library’s `ParseMultipartForm` method, with upload size constraints governed by the `Engine.MaxMultipartMemory` field. This guide explains how to configure file upload limits in the gin-gonic/gin framework based on the actual source code implementation in [`gin.go`](https://github.com/gin-gonic/gin/blob/main/gin.go) and [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go).

## Understanding Gin's Upload Size Mechanism

Gin delegates multipart parsing to the standard library but controls the memory threshold through a configurable engine property. When a handler calls `c.FormFile()` or `c.MultipartForm()`, the framework invokes `ParseMultipartForm` with the limit stored in the engine configuration.

### Default Memory Limits and Engine Configuration

The default behavior and configuration points are defined in [`gin.go`](https://github.com/gin-gonic/gin/blob/main/gin.go):

- **Default limit**: Lines 26-27 define `defaultMultipartMemory = 32 << 20` (32 MiB)
- **Engine field**: Lines 165-167 declare `MaxMultipartMemory int64` as a public field on the `Engine` struct
- **Initialization**: Line 221 sets the initial value to `defaultMultipartMemory` during engine creation

### How Parsing Works Under the Hood

When handling uploads, Gin calls `c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory)` at two key locations in [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go):

- Lines 997-1002: Inside the `FormFile` method
- Lines 1014-1015: Inside the `MultipartForm` method

The Go standard library stores multipart data up to this size **in memory**, automatically writing overflow to temporary disk files. If the request exceeds the limit and disk spooling fails (due to permissions or disk space), `ParseMultipartForm` returns an error that Gin propagates to the caller.

## Configuring Global Upload Size Limits

To apply a uniform limit across all routes, modify the `MaxMultipartMemory` field immediately after creating the engine. This value applies to every multipart request processed by that engine instance.

```go
func main() {
    r := gin.New()
    // Set global limit to 10 MiB
    r.MaxMultipartMemory = 10 << 20

    r.POST("/upload", func(c *gin.Context) {
        file, err := c.FormFile("file")
        if err != nil {
            c.String(http.StatusBadRequest, "Upload failed: %v", err)
            return
        }

        if err := c.SaveUploadedFile(file, "./uploads/"+file.Filename); err != nil {
            c.String(http.StatusInternalServerError, "Save error: %v", err)
            return
        }

        c.String(http.StatusOK, "File %s uploaded successfully", file.Filename)
    })

    r.Run(":8080")
}

```

Because `Engine` embeds `RouterGroup`, all route groups inherit the same engine reference and share this limit. Changing `MaxMultipartMemory` on the engine affects every route attached to it.

## Implementing Per-Route Upload Restrictions

While `MaxMultipartMemory` is engine-global, you can enforce stricter limits for specific endpoints by pre-validating `c.Request.ContentLength` before invoking the parser. This prevents unnecessary processing of oversized uploads.

```go
r := gin.New()
r.MaxMultipartMemory = 10 << 20 // Global 10 MiB default

// Strict endpoint with 1 MiB limit
r.POST("/small-upload", func(c *gin.Context) {
    if c.Request.ContentLength > 1<<20 {
        c.String(http.StatusRequestEntityTooLarge, "File exceeds 1 MiB limit")
        return
    }

    file, err := c.FormFile("file")
    if err != nil {
        c.String(http.StatusBadRequest, "Could not process file: %v", err)
        return
    }

    c.SaveUploadedFile(file, "./small/"+file.Filename)
    c.String(http.StatusOK, "Small file uploaded")
})

// Standard endpoint using global 10 MiB limit
r.POST("/regular-upload", func(c *gin.Context) {
    file, _ := c.FormFile("file")
    c.SaveUploadedFile(file, "./regular/"+file.Filename)
    c.String(http.StatusOK, "Regular file uploaded")
})

```

This approach allows you to define a safe global ceiling while applying stricter business logic to specific routes without modifying the engine configuration.

## Handling Upload Errors and Validation

When `ParseMultipartForm` fails due to memory constraints or disk write errors, Gin returns the error through `FormFile` or `MultipartForm`. Implement proper error handling to distinguish between size violations and other parsing failures.

```go
file, err := c.FormFile("upload")
if err != nil {
    // Handle specific error types if needed
    c.String(http.StatusBadRequest, "Upload failed: %v", err)
    return
}

```

You can also implement pre-emptive rejection by checking the content length against your limit before parsing:

```go
if c.Request.ContentLength > r.MaxMultipartMemory {
    c.String(http.StatusRequestEntityTooLarge,
        "File too large (max %d bytes)", r.MaxMultipartMemory)
    return
}

```

This prevents the framework from attempting to parse payloads that are guaranteed to exceed your configured limits.

## Saving Uploaded Files with Size Constraints

After successful parsing, persist the file using `SaveUploadedFile`, implemented in [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go) at lines 718-724. This helper copies the already-parsed multipart data to your destination path without re-reading the request body.

```go
func handleUpload(c *gin.Context) {
    // Parse with configured memory limit
    file, err := c.FormFile("document")
    if err != nil {
        c.String(http.StatusBadRequest, "Parse error: %v", err)
        return
    }

    // Validate file size if needed
    if file.Size > 5<<20 {
        c.String(http.StatusRequestEntityTooLarge, "File exceeds 5 MiB")
        return
    }

    // Save to disk
    dst := fmt.Sprintf("./uploads/%s", file.Filename)
    if err := c.SaveUploadedFile(file, dst); err != nil {
        c.String(http.StatusInternalServerError, "Save failed: %v", err)
        return
    }

    c.JSON(http.StatusOK, gin.H{"filename": file.Filename, "size": file.Size})
}

```

## Summary

- **Global configuration**: Set `Engine.MaxMultipartMemory` (default 32 MiB) to control the memory buffer for all multipart requests in [`gin.go`](https://github.com/gin-gonic/gin/blob/main/gin.go)
- **Per-route limits**: Check `c.Request.ContentLength` before calling `FormFile()` to enforce endpoint-specific restrictions
- **Parsing mechanism**: Gin calls `ParseMultipartForm` in [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go) (lines 997-1002 and 1014-1015) using the engine's configured limit
- **File persistence**: Use `SaveUploadedFile` (context.go lines 718-724) to write parsed files to disk after validation
- **Error handling**: Capture errors from `FormFile` or `MultipartForm` to handle cases where uploads exceed available memory and disk space

## Frequently Asked Questions

### What is the default file upload size limit in Gin?

According to the source code in [`gin.go`](https://github.com/gin-gonic/gin/blob/main/gin.go) lines 26-27, the default limit is 32 MiB (`defaultMultipartMemory = 32 << 20`). This represents the maximum memory buffer used before spilling to disk, not a hard file size limit.

### How do I set different upload limits for specific routes in Gin?

Because `MaxMultipartMemory` is defined on the `Engine` struct and shared across all routes, you cannot set different memory limits per route directly. Instead, check `c.Request.ContentLength` at the beginning of your handler to reject requests that exceed your desired threshold for that specific endpoint before parsing begins.

### What happens when an uploaded file exceeds the memory limit?

When a file exceeds `MaxMultipartMemory`, the Go standard library attempts to write the overflow to temporary disk files. If successful, parsing continues normally. If disk writing fails due to permissions or space constraints, `ParseMultipartForm` returns an error that Gin propagates through `FormFile` or `MultipartForm`, allowing you to handle the failure gracefully.

### Can I reject large file uploads before Gin parses the multipart form?

Yes. Check `c.Request.ContentLength` against your desired limit before calling `c.FormFile()` or `c.MultipartForm()`. If the content length exceeds your threshold, return HTTP 413 (Request Entity Too Large) immediately. This prevents the framework from consuming resources to parse payloads you intend to reject anyway.