Routing¶
In Skipper (the HTTP router and reverse proxy), routing refers to the process of matching incoming HTTP requests to routes and applying the associated filters and backend configurations. It provides multiple extension points to customize how routes are processed and how HTTP requests are handled. These extension points allow us to modify route behavior at different stages of the routing lifecycle.

Extension Points Overview¶
Skipper offers three main extension points to customizing routing behavior:
- PreProcessors - Modify or inspect routes before they are instantiated (work with route definitions)
- Filters - Process individual HTTP requests and responses during runtime (work with traffic)
- PostProcessors - Modify or inspect routes after instantiation but before applied in active routing table (work with constructed routes)
Each extension point operates at a different stage and serves different purposes. PreProcessors and PostProcessors are for route-level customization during routing table updates (i.e. in the control plane), while Filters are for request-level processing during runtime traffic handling (i.e. in the data plane).
Route Processors (PreProcessors & PostProcessors)¶
They allow us to transform, validate, or prepare routes before they become active in the routing table of the proxy.
Processing Order¶
When routes are loaded, they go through the following flow:
- DataClient loads raw route data from a source (file, database, etc.)
- PreProcessors run (can modify or inspect
eskip.Routedefinitions) - Routes are instantiated into
routing.Routeobjects and Filters are created as defined by each route - PostProcessors run (can modify or inspect instantiated routes)
- Routes become active in the routing table of the proxy
- Filters process HTTP requests/responses that match these routes
This pipeline allows PreProcessors to work with lightweight route definitions, while PostProcessors can access fully constructed route objects with filter instances and endpoint configurations. Filters operate later, during actual request processing.
PreProcessors¶
PreProcessors are interfaces that process routes before they are instantiated from their eskip.Route representation.
This allows modification or preparing pre-requisites for the route definitions before they become part of the routing table.
Interface¶
Implements the following interface:
type PreProcessor interface {
Do([]*eskip.Route) []*eskip.Route
}
Built-in PreProcessors¶
Skipper includes several built-in PreProcessors. Here are two examples.
DefaultFilters PreProcessor¶
Prepends and/or appends filters to all routes.
Use case: Add common filters like logging, metrics, or authentication to all routes without manually adding them to each route definition.
OPA PreProcessor¶
Starts Open Policy Agent instances based on route definitions that include OPA related filters. Since OPA instances may take time to start, it handles initialization here rather than blocking route instantiation.
Use case: Ensure OPA instances are created and started before OPA filter creation, to avoid delays during route instantiation.
PostProcessors¶
PostProcessors are interfaces that process routes after they are instantiated from their data representation, and before they are passed to be effective in the routing table.
Interface¶
Implements this interface:
type PostProcessor interface {
Do([]*routing.Route) []*routing.Route
}
Built-in PostProcessors¶
Skipper includes several built-in PostProcessors. Here is an example.
LoadBalancer Algorithm Provider PostProcessor¶
Initializes the load balancing algorithm for routes with LB backends.
Implementation: loadbalancer.NewAlgorithmProvider() in loadbalancer/algorithm.go
Use case: Assigns appropriate load balancing algorithms (roundRobin, random, consistentHash, powerOfRandomNChoices, etc.) to routes with LB backends based on their configuration.