A Gin middleware that automatically detects and exposes the server's hostname and scheme by inspecting the incoming http.Request
. This is particularly useful when your application runs behind proxies or load balancers, as it intelligently determines the correct public-facing URL.
- Automatic Detection: Intelligently detects scheme (HTTP/HTTPS) and host from various sources
- Proxy-Aware: Respects standard proxy headers (
X-Forwarded-Proto
,X-Forwarded-Host
) - Flexible Configuration: Support for custom headers and fallback values
- Base Path Support: Configure base paths for applications running under sub-paths
- Zero Dependencies: Only depends on Gin framework
The middleware examines the request in the following order to determine the scheme and host:
Scheme Detection:
X-Forwarded-Proto
header (or custom configured header)- Request URL scheme
- TLS connection presence
- Protocol string
- Configured default scheme (fallback)
Host Detection:
- Configured custom host header (e.g.,
X-Forwarded-Host
) X-Host
header- Request
Host
header - Request URL host
- Configured default host (fallback)
Download and install it:
go get github.com/gin-contrib/location/v2
Import it in your code:
import "github.com/gin-contrib/location/v2"
Use location.Default()
for quick setup with sensible defaults:
package main
import (
"fmt"
"net/http"
"github.com/gin-contrib/location/v2"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// Configure with default settings:
// - Scheme: defaults to "http"
// - Host: defaults to "localhost:8080"
// - Headers: X-Forwarded-Proto (scheme), X-Forwarded-Host (host)
router.Use(location.Default())
router.GET("/", func(c *gin.Context) {
url := location.Get(c)
// Access the detected URL components
fmt.Printf("Scheme: %s\n", url.Scheme) // e.g., "https"
fmt.Printf("Host: %s\n", url.Host) // e.g., "example.com"
fmt.Printf("Path: %s\n", url.Path) // e.g., ""
c.String(http.StatusOK, "Full URL: %s", url.String())
})
router.Run(":8080")
}
Customize the middleware for your specific environment:
package main
import (
"fmt"
"net/http"
"github.com/gin-contrib/location/v2"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// Custom configuration for production environment behind a proxy
router.Use(location.New(location.Config{
Scheme: "https", // Default scheme when not detected
Host: "api.example.com", // Default host when not detected
Base: "/v1", // Base path for the application
Headers: location.Headers{
Scheme: "X-Forwarded-Proto", // Header for scheme detection
Host: "X-Forwarded-Host", // Header for host detection
},
}))
router.GET("/users", func(c *gin.Context) {
url := location.Get(c)
// With Base="/v1", url.Path will be "/v1"
// Full URL will be: https://api.example.com/v1
c.JSON(http.StatusOK, gin.H{
"message": "API endpoint",
"url": url.String(),
})
})
router.Run(":8080")
}
Field | Type | Description | Default |
---|---|---|---|
Scheme |
string |
Default scheme when not detected | "http" |
Host |
string |
Default host when not detected | "localhost:8080" |
Base |
string |
Base path for the application | "" |
Headers.Scheme |
string |
Header name for scheme detection | "X-Forwarded-Proto" |
Headers.Host |
string |
Header name for host detection | "X-Forwarded-Host" |
When running behind nginx, Apache, or other reverse proxies:
router.Use(location.New(location.Config{
Scheme: "https",
Host: "your-domain.com",
Headers: location.Headers{
Scheme: "X-Forwarded-Proto",
Host: "X-Forwarded-Host",
},
}))
For applications deployed in Kubernetes with an Ingress controller:
router.Use(location.New(location.Config{
Scheme: "https",
Host: "api.myapp.com",
Base: "/api", // If your ingress routes /api to this service
Headers: location.Headers{
Scheme: "X-Forwarded-Proto",
Host: "X-Forwarded-Host",
},
}))
Generate absolute URLs in your responses:
router.GET("/users/:id", func(c *gin.Context) {
userID := c.Param("id")
baseURL := location.Get(c)
// Build absolute URL for the resource
userURL := fmt.Sprintf("%s/users/%s", baseURL.String(), userID)
c.JSON(http.StatusOK, gin.H{
"id": userID,
"self": userURL,
})
})
Returns the location middleware with default configuration (HTTP scheme, localhost:8080 host).
Returns the location middleware with custom configuration.
Retrieves the location information from the Gin context. Returns nil
if the middleware is not configured.
Returns: A *url.URL
with the following fields:
Scheme
: The detected or configured scheme (http/https)Host
: The detected or configured host (including port if present)Path
: The configured base path
Configuration structure for the middleware:
type Config struct {
Scheme string // Default scheme (e.g., "https")
Host string // Default host (e.g., "example.com:443")
Base string // Base path (e.g., "/api/v1")
Headers Headers // Custom headers for detection
}
Header configuration for scheme and host detection:
type Headers struct {
Scheme string // Header name for scheme (default: "X-Forwarded-Proto")
Host string // Header name for host (default: "X-Forwarded-Host")
}
If you're upgrading from v1, simply update your import path:
- import "github.com/gin-contrib/location"
+ import "github.com/gin-contrib/location/v2"
The API remains fully backward compatible. No code changes are required.
We welcome contributions! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Please ensure:
- All tests pass (
go test -v ./...
) - Code is formatted (
go fmt ./...
) - Linting passes (
golangci-lint run
)
This project is licensed under the MIT License - see the LICENSE file for details.