# How to Serve Static Files in Gin: 4 Methods Explained

> Learn to serve static files in Gin using four powerful methods: Static, StaticFS, StaticFile, and StaticFileFS. Effortlessly expose directories and files with Gin's HTTP router.

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

---

**Gin provides four router methods—`Static`, `StaticFS`, `StaticFile`, and `StaticFileFS`—that expose directories or individual files through the HTTP router with configurable directory listing and custom filesystem support.**

Gin, the high-performance HTTP web framework from the gin-gonic/gin repository, includes built-in utilities to serve static files in Gin without external middleware. Whether you are exposing a public assets folder, serving single files like `favicon.ico`, or shipping embedded resources with Go 1.16+, the framework's router group implementation in [`routergroup.go`](https://github.com/gin-gonic/gin/blob/main/routergroup.go) offers a concise API for mapping filesystem paths to HTTP endpoints.

## The Four Static File Helpers

The core implementation lives in **[`routergroup.go`](https://github.com/gin-gonic/gin/blob/main/routergroup.go)**, where the `RouterGroup` type defines four public methods for static asset delivery:

- **`Static(relativePath, root string)`** – Serves an entire directory at the given URL prefix. Internally, it creates a `http.FileServer` with `Dir(root, false)`, which disables directory listing.
- **`StaticFS(relativePath string, fs http.FileSystem)`** – Functions identically to `Static` but accepts any `http.FileSystem` interface, enabling in-memory or embedded filesystems.
- **`StaticFile(relativePath, filepath string)`** – Registers a single route that serves one specific file from the local filesystem.
- **`StaticFileFS(relativePath, filepath string, fs http.FileSystem)`** – Serves a single file from a custom `http.FileSystem` instead of the OS filesystem.

## How Static File Serving Works Under the Hood

### Path Validation

Both `Static` and `StaticFS` reject URL parameters (`:` or `*`) to prevent ambiguous routing conflicts. According to the source code in [`routergroup.go`](https://github.com/gin-gonic/gin/blob/main/routergroup.go) (lines 100-106), the framework panics if parameters are detected:

```go
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
    panic("URL parameters can not be used when serving a static folder")
}

```

### Handler Creation

The private `createStaticHandler` function (lines 116-124 in [`routergroup.go`](https://github.com/gin-gonic/gin/blob/main/routergroup.go)) builds the actual HTTP handler. It wraps `http.FileServer` with `http.StripPrefix` to ensure request paths align with the filesystem layout before hitting the file server.

### Directory Listing Control

Directory browsing is controlled by the `Dir` helper in **[`fs.go`](https://github.com/gin-gonic/gin/blob/main/fs.go)** (lines 38-50). When the second argument is `false`, Gin returns an `OnlyFilesFS` that silences `Readdir` calls, effectively preventing directory enumeration. Passing `true` allows standard `http.Dir` behavior with listing enabled.

### Error Handling

If a requested file cannot be opened or does not exist, Gin falls back to the router's **no-route** handlers, ensuring the client receives a 404 response rather than a raw filesystem error.

## Practical Implementation Examples

### Serve a Directory Without Listing

To expose an entire folder while preventing users from browsing its contents, use `Static`. This maps the `./public` directory to the `/static` URL prefix:

```go
router := gin.Default()

// Expose "./public" at URL "/static" (directory listing disabled)
router.Static("/static", "./public")

```

*Implementation path:* `router.Static` → `router.StaticFS` → `Dir(root, false)` → `createStaticHandler`.

### Enable Directory Listing

For development scenarios or public file repositories where you want browsers to display file indexes, use `StaticFS` with `gin.Dir(..., true)`:

```go
router := gin.Default()

// Allow the browser to list files
router.StaticFS("/files", gin.Dir("./files", true))

```

*Implementation path:* `router.StaticFS` → supplied `http.FileSystem` (here `gin.Dir(..., true)`) → `createStaticHandler`.

### Serve a Single File

To map a specific URL directly to a single file, such as a robots.txt or favicon.ico, use `StaticFile`:

```go
router := gin.Default()

// Map "/favicon.ico" directly to the local file
router.StaticFile("/favicon.ico", "./resources/favicon.ico")

```

*Implementation path:* `router.StaticFile` → `staticFileHandler` → `c.File(filepath)`.

### Serve Embedded Files (Go 1.16+)

When using Go's `embed` package to bundle assets into your binary, serve them through `StaticFileFS` or `StaticFS` with `http.FS`:

```go
import (
    "embed"
    "net/http"
    "github.com/gin-gonic/gin"
)

//go:embed assets/logo.png
var embedFS embed.FS

func main() {
    router := gin.Default()
    
    // Serve single embedded file
    fs := http.FS(embedFS)
    router.StaticFileFS("/logo.png", "assets/logo.png", fs)
    
    // Or serve entire embedded directory
    router.StaticFS("/assets", fs)
}

```

*Implementation path for single files:* `router.StaticFileFS` → `staticFileHandler` → `c.FileFromFS(filepath, fs)`.

### Global Convenience Wrappers

For quick scripts using the default engine, **[`ginS/gins.go`](https://github.com/gin-gonic/gin/blob/main/ginS/gins.go)** (lines 100-118) provides package-level functions that forward to the global router:

```go
import "github.com/gin-gonic/gin"

func main() {
    gin.Static("/assets", "./static")
    gin.StaticFile("/robots.txt", "./static/robots.txt")
    gin.Run()
}

```

These wrappers call the same underlying methods described above on the default engine instance.

## Summary

- **`router.Static`** serves directories with listing disabled by default via `Dir(root, false)`.
- **`router.StaticFS`** accepts any `http.FileSystem`, including custom implementations and `http.FS` wrappers for embedded assets.
- **`router.StaticFile`** maps individual files to specific routes without exposing the parent directory.
- **`router.StaticFileFS`** serves single files from non-standard filesystems like `embed.FS`.
- Path parameters (`:` or `*`) are forbidden in static routes and trigger a panic at registration time.
- Error handling falls through to no-route handlers, returning 404 for missing files.

## Frequently Asked Questions

### How do I enable directory browsing in Gin?

Pass `true` as the second argument to `gin.Dir` when calling `StaticFS`. For example: `router.StaticFS("/files", gin.Dir("./files", true))`. This uses the standard `http.Dir` implementation instead of the restricted `OnlyFilesFS`.

### Can I use URL parameters in static file routes?

No. Gin explicitly forbids URL parameters (segments containing `:` or `*`) in static file routes. The framework checks for these characters in [`routergroup.go`](https://github.com/gin-gonic/gin/blob/main/routergroup.go) and panics during registration to prevent routing ambiguities between static handlers and parameterized routes.

### What happens when a requested static file does not exist?

Gin delegates to the router's no-route handlers, resulting in a 404 Not Found response. The `createStaticHandler` function does not implement custom error responses; it relies on the standard `http.FileServer` behavior filtered through Gin's handler chain.

### How do I serve files from an embedded filesystem using Go 1.16 embed?

Wrap your `embed.FS` with `http.FS()`, then pass it to either `StaticFS` for directories or `StaticFileFS` for individual files. For example: `router.StaticFS("/assets", http.FS(embedFS))`. This integration allows you to ship a single binary containing both your application and its static assets.