Skip to main content

Routes

A route is the core entity of the Otoroshi proxy engine. It defines a unique routing rule: which incoming requests to match (the frontend), where to forward them (the backend), and what processing to apply along the way (the plugin chain).

Routes are the building blocks of your API gateway configuration. Every HTTP request that reaches Otoroshi is matched against the set of enabled routes. When a match is found, the request flows through the route's plugin pipeline and is ultimately forwarded to one of the backend targets.

How a route works

A route is made of three main parts:

                          ┌─────────────────────────────────────┐
Incoming request ───► │ Frontend (match the request) │
│ Plugin chain (process the request) │
│ Backend (forward the request) │
└──────────────────┬──────────────────┘


Backend targets
  1. Frontend — defines the matching criteria: domain names, paths, HTTP methods, headers, query parameters, and cookies. If an incoming request satisfies all the criteria, the route is selected.
  2. Backend — defines where matched requests are forwarded: a list of target servers with load balancing, health checks, timeouts, retries, and TLS settings.
  3. Plugins — an ordered chain of plugins that process the request and response as they flow through the route. Plugins handle cross-cutting concerns like authentication, rate limiting, header manipulation, CORS, caching, etc.

Request lifecycle

When a request hits Otoroshi, it goes through the following steps:

  1. Route matching — the router compares the request against all enabled routes. It matches on domain, path, method, headers, query params, and cookies. The router is optimized to handle thousands of routes without performance impact.
  2. Pre-route plugins — plugins that run before the request is routed (e.g., early redirections, pre-routing logic).
  3. Access validation plugins — plugins that decide whether the request is allowed to proceed (e.g., API key validation, IP filtering, OAuth token verification).
  4. Request transformation plugins — plugins that modify the request before it is sent to the backend (e.g., add/remove headers, rewrite the body).
  5. Backend call — Otoroshi selects a target using the configured load balancing strategy and forwards the (possibly transformed) request.
  6. Response transformation plugins — plugins that modify the response before sending it back to the client (e.g., add security headers, transform the body).
  7. Client response — the final response is streamed back to the caller.

Each step is recorded in an execution report (when debug_flow or export_reporting is enabled), making it easy to understand exactly what happened during a request. See engine docs for details.

Frontend matching in depth

The frontend controls which requests a route captures. All specified criteria must match (AND logic).

Domain and path patterns

Domains and paths are combined in the domains field (e.g., api.example.com/v1/users). Several patterns are supported:

PatternExampleMatches
Exact domainapi.example.comonly api.example.com
Wildcard subdomain*.example.comapi.example.com, admin.example.com, etc.
Wildcard segmentapi.*.comapi.example.com, api.test.com, etc.
Plain pathapi.example.com/users/users, /users/123 (prefix mode) or just /users (exact mode)
Wildcard pathapi.example.com/users/*/bills/users/42/bills, /users/abc/bills
Named path paramapi.example.com/users/:id/bills/users/42/bills (captures id=42)
Regex path paramapi.example.com/users/$id<[0-9]+>/bills/users/42/bills but not /users/abc/bills

Path matching modes

  • Prefix mode (exact: false, the default) — /api/users matches /api/users, /api/users/123, /api/users/123/orders, etc.
  • Exact mode (exact: true) — /api/users matches only /api/users and nothing else.

Strip path

When strip_path is true (the default), the matched path prefix is removed before forwarding to the backend. For example:

Frontend pathIncoming requeststrip_path: truestrip_path: false
/api/users/api/users/123backend receives /123backend receives /api/users/123
/v1/v1/ordersbackend receives /ordersbackend receives /v1/orders

Combined with the backend root path, this gives full control over URL rewriting. If root is /legacy-api and strip_path is true, then a request to /api/users/123 on a route matching /api is forwarded to /legacy-api/users/123.

Named path params and URL rewriting

When using named path parameters (:id or $id<regex>), captured values are available for URL rewriting on the backend side. For example:

  • Frontend: api.example.com/users/$id<[0-9]+>/bills
  • Backend rewrite target: /apis/v1/basic_users/${req.pathparams.id}/all_bills

This allows completely reshaping URLs between the client-facing API and the actual backend.

Headers, query parameters, and cookies support several matching modes:

SyntaxMeaning
"exact_value"The value must match exactly
"Regex(pattern)"The value must match the regex pattern
"Wildcard(val*)"The value must match the wildcard pattern
"Exists()" or "IsDefined()"The header/param/cookie must be present (any value)
"NotDefined()"The header/param/cookie must not be present

If the map is empty, any value is accepted (no constraint).

Plugin pipeline

Plugins are the primary extension mechanism. Each plugin in the chain can inspect, modify, or short-circuit the request at a specific phase of the lifecycle. A route's plugin list is an ordered array of plugin slots, each with its own configuration.

Key concepts:

  • Ordering: plugins execute in array order by default. Use plugin_index to set explicit positions per phase (e.g., { "pre_route": 0, "validate_access": 1 }).
  • Scoping: each plugin can have include/exclude path patterns so it only applies to certain sub-paths of the route.
  • Per-plugin debug: enable debug: true on a single plugin slot to get detailed execution info for that plugin only, without turning on debug_flow for the entire route.
  • Listener binding: a plugin can be restricted to specific HTTP listeners via bound_listeners.
  • Enable/disable: toggle a plugin with enabled: true/false without removing it from the configuration.

For the full list of available plugins, see built-in plugins.

UI page

You can find all routes here

Properties

PropertyTypeDefaultDescription
idstringUnique identifier of the route
namestringDisplay name of the route
descriptionstringDescription of the route
tagsarray of string[]Tags for categorization and API automation
metadataobject{}Key/value metadata. Some keys are reserved
enabledbooleantrueWhether the route is active. Disabled routes are ignored by the router
debug_flowbooleanfalseEnable debug flow. Execution reports will contain all input/output values. See engine docs
capturebooleanfalseEnable request/response capture. Generates events with full request content. Use with caution! See engine docs
export_reportingbooleanfalseExport execution reports for each request via data exporters. See engine docs
groupsarray of string["default"]Service groups this route belongs to. Used for API key authorization
bound_listenersarray of string[]List of HTTP listener IDs this route is bound to. When a listener is exclusive, only bound routes are served on its port
frontendobjectFrontend configuration (how the router matches this route). See below
backendobjectBackend configuration (where to forward requests). See backends
backend_refstringnullReference to a global stored backend by ID. If set, takes precedence over inline backend
pluginsobjectPlugin chain configuration. See below

Reserved metadata

Some metadata keys are reserved for Otoroshi internal use:

KeyDescription
otoroshi-core-user-facingIs this a user-facing app for the Snow Monkey
otoroshi-core-use-akka-http-clientUse the pure Akka HTTP client
otoroshi-core-use-netty-http-clientUse the pure Netty HTTP client
otoroshi-core-use-akka-http-ws-clientUse the modern WebSocket client
otoroshi-core-issue-lets-encrypt-certificateEnable Let's Encrypt certificate issuance for this route (true/false)
otoroshi-core-issue-certificateEnable certificate issuance for this route (true/false)
otoroshi-core-issue-certificate-caID of the CA cert to generate the certificate
otoroshi-core-openapi-urlOpenAPI spec URL for this route
otoroshi-core-envEnvironment for this route (legacy)
otoroshi-deployment-providersRelay routing: infrastructure providers
otoroshi-deployment-regionsRelay routing: network regions
otoroshi-deployment-zonesRelay routing: network zones
otoroshi-deployment-dcsRelay routing: datacenters
otoroshi-deployment-racksRelay routing: racks

Frontend configuration

The frontend defines how the Otoroshi router matches incoming requests to this route.

PropertyTypeDefaultDescription
domainsarray of string[]Matched domains and paths. Supports wildcards in domain and path, and named path params (e.g., api.oto.tools/users/$id<[0-9]+>)
strip_pathbooleantrueStrip the matched path from the forwarded request
exactbooleanfalsePerform exact path matching. If false, matches on /path*
headersobject{}Required HTTP headers to match. If empty, any headers match
queryobject{}Required query parameters to match. If empty, any query params match
cookiesobject{}Required cookies to match. If empty, any cookies match
methodsarray of string[]Allowed HTTP methods. If empty, any method matches

For more information about routing, check the engine documentation.

Frontend JSON example

{
"domains": [
"api.oto.tools/users"
],
"strip_path": true,
"exact": false,
"headers": {},
"query": {},
"cookies": {},
"methods": ["GET", "POST"]
}

Backend configuration

The backend field defines the target servers to forward requests to. For detailed documentation, see backends.

Alternatively, use backend_ref to reference a global stored backend by ID, making the backend reusable across multiple routes.

Plugins

The plugins field contains the list of plugins applied on this route. Each plugin definition has the following structure:

PropertyTypeDefaultDescription
enabledbooleantrueWhether the plugin is active
debugbooleanfalseEnable debug output for this specific plugin
pluginstringThe plugin identifier (e.g., cp:otoroshi.next.plugins.Redirection)
includearray of string[]Path patterns to include. If empty, all paths are included
excludearray of string[]Path patterns to exclude. If empty, no paths are excluded
configobject{}Plugin-specific configuration
plugin_indexobjectnullExplicit plugin execution order. If not provided, array order is used
bound_listenersarray of string[]Listener-specific plugin binding

Plugin JSON example

{
"enabled": true,
"debug": false,
"plugin": "cp:otoroshi.next.plugins.Redirection",
"include": [],
"exclude": [],
"config": {
"code": 303,
"to": "https://www.otoroshi.io"
},
"plugin_index": {
"pre_route": 0
}
}

For the full list of available plugins, see built-in plugins.

Complete route JSON example

{
"id": "route_users_api",
"name": "Users API",
"description": "Route for the users microservice",
"tags": ["users", "api"],
"metadata": {},
"enabled": true,
"debug_flow": false,
"capture": false,
"export_reporting": false,
"groups": ["default"],
"bound_listeners": [],
"frontend": {
"domains": ["api.oto.tools/users"],
"strip_path": true,
"exact": false,
"headers": {},
"query": {},
"cookies": {},
"methods": []
},
"backend": {
"targets": [
{
"id": "target_1",
"hostname": "users-service.internal",
"port": 8080,
"tls": false,
"weight": 1,
"protocol": "HTTP/1.1"
}
],
"root": "/",
"rewrite": false,
"load_balancing": { "type": "RoundRobin" }
},
"backend_ref": null,
"plugins": {
"slots": [
{
"plugin": "cp:otoroshi.next.plugins.OverrideHost",
"enabled": true,
"include": [],
"exclude": [],
"config": {}
}
]
}
}

Admin API

GET    /api/routes           # List all routes
POST /api/routes # Create a route
GET /api/routes/:id # Get a route
PUT /api/routes/:id # Update a route
DELETE /api/routes/:id # Delete a route
PATCH /api/routes/:id # Partially update a route