zalando/skipper: an HTTP router and reverse proxy driven by eskip route files
An HTTP router and reverse proxy for service composition, including use cases like Kubernetes Ingress
At a glance
- What is it?
- Skipper is a Go HTTP router and reverse proxy built for service composition, with eskip as its routing language and Kubernetes Ingress among its data sources. This review covers the route mechanism, installation, the limits of the default binary, and who should pick something else.
- Who is it for?
- Adopt Skipper if your routing needs per-route filters and you are willing to write eskip or a Go extension; the default binary covers simple file-based routing, but authentication, rate limiting and cache behavior are documented as library-level features, so verify the package and package path before you plan around them. Avoid it if you want a config-file-only reverse proxy with no route language to learn.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What zalando/skipper solves, and who it is for
Skipper is an HTTP router and reverse proxy whose stated design goal is service composition. The README says it is built to handle more than 300k HTTP route definitions with detailed lookup conditions, and that routes are identified by request properties such as path, method, host and headers. That is a different target from a proxy configured by a handful of static upstream blocks. The unit of configuration is the route, and each route can carry its own filters, so request and response modification is scoped per route rather than applied globally.
The intended audience is visible in the repository layout rather than in a marketing sentence. Top-level directories include dataclients, eskip, filters, predicates, routing, routesrv, loadbalancer, ratelimit, jwt, otel and secrets. A team that only wants to forward traffic to two backends does not need most of that. A team that wants to attach an authentication check, a header rewrite and a rate limit to one specific path while leaving the rest of the fleet untouched is exactly who the filter model is aimed at. The README also names the Kubernetes Ingress use case directly, and links to a separate controller project for routing public traffic to a Skipper fleet.
How eskip routes and filters compose request flow
The configuration language is eskip, described in the README as a descriptive configuration language designed for routing rules. A route is a named expression: a matcher on the left, an arrow, and a backend or shunt on the right. The README gives this example: hello: Path("/hello") -> "https://www.example.org". The name before the colon is the route id, Path is a predicate, and the quoted string is the backend.
Filters are the second half of the mechanism. The README states that Skipper allows modification of requests and responses with filters that are independently configured for each route. In eskip syntax that means filters sit between the predicate and the backend, so the same backend can be reached through routes with different request and response transformations. This is the composition part: routing decisions and traffic shaping are expressed in one rule rather than spread across a proxy config and an application.
Route data does not have to come from a file. The README lists etcd, Kubernetes Ingress, static files, a route string, and custom configuration sources as supported data sources, and says routing rules update without downtime. The repository adds routesrv, described as a proxy that omits kube-apiserver overload by using the Etag header to reduce CPU used in the data plane, and a webhook that validates Kubernetes manifests. Two optional behaviors are worth noting. Skipper can act as a final endpoint, called a shunt, for example as a static file server or a mock backend for diagnostics. It also streams incoming requests and backend responses simultaneously, and supports HTTP/1.1, H2C and HTTP/2.
Installing skipper and serving a first route
The README states that building and running Skipper requires only the latest version of Go. For a prebuilt binary, it points at the releases page and gives a download example. The commands below are copied from that example; the version string in the example is the one the README uses, so substitute the current release tag from the releases page.
curl -LO https://github.com/zalando/skipper/releases/download/v0.27.92/skipper-v0.27.92-linux-amd64.tar.gz
tar xzf skipper-v0.27.92-linux-amd64.tar.gz
mv skipper-v0.27.92-linux-amd64/* $GOBIN/
skipper -versionThe README shows the expected output as a version line containing the tag, a commit hash and the Go runtime version. If $GOBIN is not on your PATH, the final command will not resolve.
Building from source uses the repository Makefile. The README gives this sequence, and the Makefile confirms a skipper target that writes the binary to bin/skipper.
git clone https://github.com/zalando/skipper.git
cd skipper
make
./bin/skipper -versionThe Makefile also defines separate targets for eskip, webhook and routesrv, so those binaries are built individually when needed.
For a first route, the README creates a file and starts the proxy against it. The default listener is port 9090.
echo 'hello: Path("/hello") -> "https://www.example.org"' > example.eskip
skipper -routes-file example.eskip &
curl localhost:9090/helloThe README notes that the route file syntax can be checked first with eskip check, which logs nothing when the file is valid and a descriptive error otherwise. Docker is also documented: the image is registry.opensource.zalan.do/teapot/skipper:latest, and the README's example mounts a .eskip file, publishes ports 9090 and 9911, and passes -routes-file to the container.
The default binary is not the whole project
The README is explicit that the shipped executable contains only a few built-in filters, and that the primary use case is to be extended with custom filters, predicates or data sources. That sentence should shape an adoption decision more than any feature list. If the filter you need is not built in, the answer is not a configuration flag; it is writing Go against the library and either rebuilding or loading a plugin. The repository keeps _test_plugins and _test_plugins_fail directories with compiled .so fixtures, and the Makefile lists them as build targets, which shows plugins are a real path and also that plugin builds are part of the project's own test surface. The README links a plugins repository and plugin documentation for that route.
A second limitation is the shape of the documentation. The README's feature list claims authentication and authorization without external oauth-proxy or OpenPolicyAgent containers, cluster rate limits with no instance-local storage, Letsencrypt with remote storage, and a tiered cache with local and remote storage. Those claims are made at the level of the project, and the README points readers to pkg.go.dev for details rather than showing configuration. The go.mod file lists dependencies consistent with those features, including OPA, the OPA Envoy plugin, go-oidc, go-jose, JWT libraries, Redis, and the Envoy control plane. Seeing the dependencies is not the same as seeing the intended configuration, and the README does not document rollback for a bad route update either, only that updates happen without downtime. Treat the advanced features as library capabilities to verify against the package documentation before you design around them.
A third boundary is scope. Skipper is an HTTP router and reverse proxy. The README mentions HTTP/1.1, H2C and HTTP/2, and the repository contains a fastcgiserver package, but nothing in the README describes TCP or UDP stream proxying as a first-class mode. If your problem is arbitrary L4 forwarding, this is the wrong tool.
Skipper against a file-configured proxy such as nginx
The closest familiar alternative is nginx: a reverse proxy configured by declarative files, with a mature module ecosystem. The difference in approach is where the logic lives. nginx configuration is a hierarchy of server and location blocks with directives; Skipper configuration is a flat set of named routes, each an expression with its own filter chain. Adding a header rewrite to one path in nginx means editing a location block; in Skipper it means adding a filter to that route, and the same filter can appear on other routes without duplicating a block hierarchy.
The second difference is dynamic route sources. The README lists etcd, Kubernetes Ingress, static files, a route string and custom sources as ways to feed routes, and states that rules update without downtime. nginx can be reloaded, and third-party modules exist for service discovery, but route data as a pluggable data source is the core design here, not an add-on. If your routes already live in etcd or in Kubernetes objects, that matters.
The trade-off runs the other way too. nginx has a configuration surface that most engineers already know and a large body of operational documentation. Skipper asks you to learn eskip and, for anything beyond the built-in filters, Go. For a small static proxy, that is a poor trade. For a fleet where routing rules change constantly and per-route behavior matters, the expression model is the point.
Maintenance cadence, licence and upgrade cost
The repository is not archived, and the last push was on 2026-09-23. Releases v0.28.1, v0.28.2 and v0.28.3 all landed on 2026-09-22 and 2026-09-23, so the project is receiving changes at a steady pace. The version numbers stay in the 0.x range, which is worth factoring into an upgrade policy: a minor bump can carry behavior changes, and the release notes are the place to check rather than assuming compatibility.
The README's licence badge points to Apache 2.0, and the repository has a LICENSE file at the top level. The project metadata given for this repository records the licence as NOASSERTION, which means the automated classification did not confirm a standard identifier. Those two signals disagree, so anyone embedding Skipper in a distributed product should read the LICENSE file and the repository's own packaging and delivery files rather than relying on the badge. Nothing here is legal advice; the point is that the licence question is answerable from the repository and should be answered there.
Upgrade cost has a concrete shape. The default binary is small, so upgrading the binary is cheap. If you extend Skipper with custom filters, predicates or data sources, your code compiles against the library at github.com/zalando/skipper, and a 0.x minor release can move that surface. Plugin users carry an additional constraint: the Makefile builds .so fixtures, and Go plugins are sensitive to the toolchain and dependency versions used on both sides. Budget for a rebuild and a route regression check on each bump, not just a container tag change.
Editorial conclusion
Adopt Skipper if your routing needs per-route filters and you are willing to write eskip or a Go extension; the default binary covers simple file-based routing, but authentication, rate limiting and cache behavior are documented as library-level features, so verify the package and package path before you plan around them. Avoid it if you want a config-file-only reverse proxy with no route language to learn. Before committing, clone the repository, run make, and check that the filters you need are listed in the built-in filter documentation, because custom filters require importing the library rather than using the released binary.
Frequently asked questions
What is zalando/skipper?
It is an HTTP router and reverse proxy for service composition, written in Go, that identifies routes by request properties such as path, method, host and headers and lets you attach filters per route. It can also run as a Kubernetes Ingress controller.
How do I install zalando/skipper?
The README gives two paths: download a binary tgz from the releases page and move the extracted files into $GOBIN, or clone the repository and run make, which produces ./bin/skipper. Only the latest version of Go is required to build it.
How do I run zalando/skipper with a routes file?
Create an eskip file such as 'hello: Path("/hello") -> "https://www.example.org"', then start the binary with -routes-file example.eskip. The README's example serves on localhost:9090, and eskip check validates the file syntax before you start.
Does zalando/skipper work as a Kubernetes Ingress controller?
Yes. The README lists Kubernetes Ingress as a data source and states that Skipper can serve as an Ingress controller without reloads. It also ships a webhook to validate Kubernetes manifests and routesrv to reduce load on kube-apiserver.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zalando-skipper)