Serve static files

Use the standard library's `http.FileServer` wrapped around `http.Dir` to serve static assets directly from your file system.

Use the standard library's http.FileServer wrapped around http.Dir to serve static assets directly from your file system. For production, always prefix the handler with a specific path and consider using a reverse proxy like Nginx for better performance and security.

Here is a minimal example serving files from a ./public directory at the /static URL prefix:

package main

import (
	"log"
	"net/http"
	"path/filepath"
)

func main() {
	// Define the directory to serve
	fs := http.FileServer(http.Dir("./public"))

	// Strip the "/static" prefix so the file system sees the correct path
	http.Handle("/static/", http.StripPrefix("/static/", fs))

	// Optional: Serve index.html for the root path
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filepath.Join("./public", "index.html"))
	})

	log.Println("Server starting on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

If you need to serve files from a custom directory structure or handle specific MIME types, you can wrap the handler logic. However, for most applications, http.StripPrefix is essential to prevent path traversal attacks and ensure the URL path matches the file system path.

For high-traffic production environments, it is often better to serve static files via a dedicated web server like Nginx rather than Go. This offloads I/O from your application and allows you to leverage HTTP/2, gzip compression, and caching headers more efficiently.

Example Nginx configuration to serve the same ./public directory:

server {
    listen 80;
    server_name example.com;

    # Serve static files from /var/www/public
    location /static/ {
        alias /var/www/public/;
        # Enable compression and caching
        gzip on;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Proxy API requests to your Go app
    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

In this setup, your Go application only handles dynamic routes, while Nginx handles the heavy lifting for static assets. This separation improves scalability and reduces the memory footprint of your Go process.