Bastion

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 /123

This 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/42

AddPrefix 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/123

When 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"}},
})
OperationBehavior
AddAppends headers. Does not overwrite existing values.
SetSets headers. Overwrites any existing value.
RemoveDeletes 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: /profile

When 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:

HeaderSource
X-Auth-SubjectAuthenticated subject
X-Auth-ProviderProvider name
X-Auth-ScopesComma-separated scopes
X-Auth-RoleRole claim
X-Auth-EmailEmail claim

These are set automatically by the auth subsystem and do not need to be configured in the header policy.

On this page