Request Transformation
Path rewriting, header manipulation, and prefix stripping.
Bastion transforms requests and responses as they pass through the proxy. Transformation rules are configured per-route and allow path rewriting, header manipulation, prefix stripping, and host header control.
Path Rewriting
Three path manipulation modes are available, applied during request forwarding.
Strip Prefix
Remove the matched route prefix before forwarding to the upstream:
bastion.WithRoute(bastion.RouteConfig{
Path: "/api/v1/users/*",
StripPrefix: true,
Targets: []bastion.TargetConfig{{URL: "http://users-svc:8080"}},
})
// Client: GET /api/v1/users/123
// Upstream: GET /123This is the most common transformation. When StripPrefix is true, the gateway removes the route's Path prefix from the request URL before forwarding.
Add Prefix
Prepend a path segment to the request after stripping:
bastion.WithRoute(bastion.RouteConfig{
Path: "/legacy/*",
StripPrefix: true,
AddPrefix: "/api/v2",
Targets: []bastion.TargetConfig{{URL: "http://backend:8080"}},
})
// Client: GET /legacy/items/42
// Upstream: GET /api/v2/items/42AddPrefix is applied after StripPrefix, allowing you to remap path namespaces.
Rewrite Path
Apply a regex-based path rewrite using capture groups:
bastion.WithRoute(bastion.RouteConfig{
Path: "/old-api/*",
RewritePath: "/new-api/$1",
Targets: []bastion.TargetConfig{{URL: "http://backend:8080"}},
})
// Client: GET /old-api/users/123
// Upstream: GET /new-api/users/123When RewritePath is set, it takes precedence over StripPrefix and AddPrefix. The $1 placeholder refers to the wildcard capture from the route path.
Header Manipulation
Route-Level Headers
Each route carries a HeaderPolicy with three operations applied to the upstream request:
bastion.WithRoute(bastion.RouteConfig{
Path: "/api/*",
Headers: bastion.HeaderPolicy{
Add: map[string]string{"X-Gateway": "bastion", "X-Request-Start": "{{.Timestamp}}"},
Set: map[string]string{"Host": "internal.api.example.com"},
Remove: []string{"X-Debug", "X-Internal-Token"},
},
Targets: []bastion.TargetConfig{{URL: "http://backend:8080"}},
})| Operation | Behavior |
|---|---|
Add | Appends headers. Does not overwrite existing values. |
Set | Sets headers. Overwrites any existing value. |
Remove | Deletes headers from the request. |
Operations are applied in order: Add, Set, Remove.
Transform Config
For more control, routes support a TransformConfig that allows separate header policies for requests and responses:
bastion.WithRoute(bastion.RouteConfig{
Path: "/api/*",
Transform: &bastion.TransformConfig{
RequestHeaders: bastion.HeaderPolicy{
Set: map[string]string{"X-Forwarded-Proto": "https"},
Remove: []string{"Cookie"},
},
ResponseHeaders: bastion.HeaderPolicy{
Add: map[string]string{"X-Served-By": "bastion"},
Remove: []string{"Server", "X-Powered-By"},
},
},
Targets: []bastion.TargetConfig{{URL: "http://backend:8080"}},
})- RequestHeaders -- applied to the request before forwarding to the upstream.
- ResponseHeaders -- applied to the upstream response before sending to the client.
Host Header Rewriting
By default, the proxy forwards the original Host header from the client. To override the Host header sent to the upstream:
bastion.WithRoute(bastion.RouteConfig{
Path: "/api/*",
Headers: bastion.HeaderPolicy{
Set: map[string]string{
"Host": "backend.internal.svc",
},
},
Targets: []bastion.TargetConfig{{URL: "http://backend:8080"}},
})This is necessary when the upstream validates the Host header or uses virtual host routing.
Prefix Stripping with Discovery
When service discovery generates routes with auto-prefixes (e.g., /users/*), the StripPrefix setting in the discovery configuration controls whether the prefix is stripped:
bastion.WithDiscovery(bastion.DiscoveryConfig{
AutoPrefix: true,
PrefixTemplate: "/{{.ServiceName}}",
StripPrefix: true, // Strip the auto-prefix before forwarding
})
// Discovered route: /users/profile -> Upstream: /profileWhen StripPrefix is false, the auto-prefix is preserved in the upstream request.
Common Transformation Patterns
API Versioning
Route multiple API versions to different backends:
bastion.WithRoute(bastion.RouteConfig{
Path: "/api/v1/*",
StripPrefix: true,
AddPrefix: "/api",
Targets: []bastion.TargetConfig{{URL: "http://api-v1:8080"}},
})
bastion.WithRoute(bastion.RouteConfig{
Path: "/api/v2/*",
StripPrefix: true,
AddPrefix: "/api",
Targets: []bastion.TargetConfig{{URL: "http://api-v2:8080"}},
})Security Header Injection
Add security headers to all responses:
bastion.WithRoute(bastion.RouteConfig{
Path: "/*",
Transform: &bastion.TransformConfig{
ResponseHeaders: bastion.HeaderPolicy{
Set: map[string]string{
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
},
},
},
Targets: []bastion.TargetConfig{{URL: "http://backend:8080"}},
})Removing Upstream Headers
Strip internal headers from upstream responses before they reach clients:
Transform: &bastion.TransformConfig{
ResponseHeaders: bastion.HeaderPolicy{
Remove: []string{
"Server",
"X-Powered-By",
"X-AspNet-Version",
"X-Debug-Info",
},
},
}Forwarding Client Identity
When authentication is enabled and ForwardHeaders is true, the gateway automatically adds identity headers:
| Header | Source |
|---|---|
X-Auth-Subject | Authenticated subject |
X-Auth-Provider | Provider name |
X-Auth-Scopes | Comma-separated scopes |
X-Auth-Role | Role claim |
X-Auth-Email | Email claim |
These are set automatically by the auth subsystem and do not need to be configured in the header policy.