CEL expressions reference

Uses: Kong Gateway

Common Expression Language (CEL) is a lightweight expression language used across Kong Gateway’s CEL-driven plugin config for plugin conditions and dynamic plugin configuration fields.

This page is the shared reference for that language, its available fields, and its types. See the linked pages for how each mechanism works.

Expression syntax

A predicate is the basic unit of an expression and compares a field against a value:

http.method == "GET"

This predicate has the following structure:

Object

Description

Example

Field A value extracted from the incoming request or Kong Gateway context. An absent field may return null or cause a runtime error depending on the field type. See Null handling. http.method
Value The value the field is compared against. Can be a constant (string, int, bool, null) or another field. The value can appear on either side of the predicate. "GET"
Operator Defines the comparison to perform between the field and the value. ==
Predicate Compares a field against a value using the given operator. Returns true if the comparison passes, false if it does not. http.method == "GET"

Combining predicates

Multiple predicates can be combined using logical operators:

Operator

Description

Example

&& Logical AND — true if both sides are true. http.method == "GET" && net.dst.port == 443
|| Logical OR — true if either side is true. http.method == "GET" || http.method == "POST"
! Logical NOT — inverts the result. !(http.method == "DELETE")
() Parentheses — control evaluation order. (http.method == "GET" || http.method == "POST") && net.dst.port == 443

Available fields

Plugin conditions and other CEL-driven plugin config support HTTP request fields, Kong Gateway context fields, and plugin state fields.

HTTP request fields

These fields reflect the state of the incoming HTTP request at the time the expression is evaluated. Higher-priority plugins may have already modified these values (for example, by rewriting a header or query parameter) before the expression is evaluated.

Field

Type

Description

Example

http.method string The HTTP method of the incoming request, for example "GET" or "POST". http.method == "POST"
http.host string The Host header of the incoming request. http.host == "internal.example.com"
http.path string The normalized request path, without query parameters. http.path.startsWith("/api/v2")
http.path_segments list<string> The path split on /, with empty segments excluded. For example, /a/b/c yields ["a", "b", "c"]. Individual segments can be accessed by index: http.path_segments[0] returns "a". Membership can be tested with in. "admin" in http.path_segments
http.headers.<header_name> string The value of the specified request header. Header names are always normalized to lowercase with underscores, so X-My-Header becomes http.headers.x_my_header. Returns the first value if the header has multiple values. Returns null if the header is absent. http.headers.x_version == "2"
http.queries.<param_name> string The value of the specified query parameter. Returns the first value if the parameter appears multiple times. Returns null if the parameter is absent. http.queries.auth == "required"
http.headers_list.<header_name> list<string> All values of the specified request header as a list. Returns null if the header is absent. http.headers_list.x_roles != null && "editor" in http.headers_list.x_roles
http.queries_list.<param_name> list<string> All values of the specified query parameter as a list. Returns null if the parameter is absent. http.queries_list.tag != null && "featured" in http.queries_list.tag
net.protocol string The protocol of the Route, for example "http" or "https". net.protocol == "https"
net.tls.sni string The server name from the TLS ClientHello packet, if the connection is over TLS. Returns null for non-TLS connections. net.tls.sni == "api.example.com"
net.src.ip string The IP address of the client. net.src.ip == "10.0.0.1"
net.src.port int The port used by the client to connect. net.src.port > 1024
net.dst.ip string The listening IP address where Kong Gateway accepted the connection. net.dst.ip == "192.168.1.1"
net.dst.port int The listening port where Kong Gateway accepted the connection. net.dst.port == 443

Gateway context fields

The following fields are populated during plugin execution and reflect the Gateway context at the time the expression is evaluated.

Field

Type

Description

Example

route.id string The UUID of the matched Route. null for global plugins or Service-scoped plugins when no Route is matched. route.id == "a1b2c3d4-..."
route.name string The name of the matched Route. null when no Route is matched. route.name == "payments-route"
route.tags list<string> Tags assigned to the matched Route. null if no Route is matched or the Route has no tags. route.tags != null && "internal" in route.tags
service.id string The UUID of the matched Service. null for global plugins when no Service is matched. service.id == "a1b2c3d4-..."
service.name string The name of the matched Service. null when no Service is matched. service.name == "payments-service"
service.tags list<string> Tags assigned to the matched Service. null if no Service is matched or the Service has no tags. service.tags != null && "internal" in service.tags
consumer.id string The UUID of the authenticated Consumer, if one has been identified by a higher-priority plugin. null if no Consumer is matched. consumer.id == "a1b2c3d4-..."
consumer.username string The username of the authenticated Consumer. null if no Consumer is matched or the Consumer has no username. consumer.username == "alice"
consumer.custom_id string The custom ID of the authenticated Consumer. null if no Consumer is matched or the Consumer has no custom ID. consumer.custom_id == "user-123"
consumer.tags list<string> Tags assigned to the authenticated Consumer. null if no Consumer is matched or the Consumer has no tags. consumer.tags != null && "vip" in consumer.tags
consumer_group.names list<string> Names of Consumer Groups matched for this request. null if no Consumer Groups are matched. consumer_group.names != null && "premium" in consumer_group.names
consumer_group.ids list<string> UUIDs of Consumer Groups matched for this request. null if no Consumer Groups are matched. consumer_group.ids != null && "a1b2c3d4-..." in consumer_group.ids
kong.ctx.shared Map Values from the kong.ctx.shared table, set by higher-priority plugins during the access phase. Inner keys must be checked with has() before access, as they are not pre-populated. See Null handling. has(kong.ctx.shared.my_flag) && kong.ctx.shared.my_flag == "enabled"
principal.id string The UUID of the authenticated Principal. null if no Principal is authenticated. principal.id == "a1b2c3d4-..."
principal.display_name string The display name of the authenticated Principal. null if no Principal is authenticated. principal.display_name == "alice"
principal.metadata Map Attributes of the authenticated Principal’s metadata. null if no Principal is authenticated. Inner keys must be checked with has() before access. See Null handling. has(principal.metadata.department) && principal.metadata.department == "finance"

consumer.* and consumer_group.* fields are only populated after an authentication plugin (such as Key Auth or Basic Auth) has run. On a plugin condition, this means the condition must be on a plugin with a lower priority than the authentication plugin.

Plugin state fields

The following fields reflect the configured state of other plugins in the same Gateway workspace or control plane at the time the expression is evaluated.

Field

Type

Description

Example

plugins.<plugin_name>.is_matched boolean
  • true if the plugin is configured and its Route or Service scope matches this request (Consumer scope excluded).
  • false if the plugin is configured but its scope does not match.
  • null if the plugin isn’t configured.
plugins.key_auth.is_matched == true
plugins.<plugin_name>.priority int The priority of the matched plugin. null if the plugin isn’t matched. plugins.key_auth.priority > 1000
plugins.<plugin_name>.access_has_executed boolean
  • true if the plugin’s access phase has already executed at the time this field is evaluated.
  • false if the plugin is matched but its access phase has not run yet.
  • null if the plugin isn’t configured.
plugins.key_auth.access_has_executed == true

Null handling

How null values behave depends on the field type.

The following fields return null when a value isn’t set:

  • http.headers.*
  • http.queries.*
  • http.headers_list.*
  • http.queries_list.*
  • net.tls.sni
  • All context fields (consumer.*, route.*, service.*, principal.*, consumer_group.*, plugins.*)

You can compare these directly using null:

consumer.id != null
route.tags != null && "internal" in route.tags

The kong.ctx.shared and principal.metadata fields are maps whose inner keys are not pre-populated by Kong Gateway. Accessing a missing key in these maps causes a runtime error. Before accessing a key, use has() to check for its existence:

has(kong.ctx.shared.my_flag) && kong.ctx.shared.my_flag == "enabled"

How that runtime error is handled once it occurs (for example, whether it can be caught with default()) depends on which CEL-driven mechanism the expression belongs to. See Plugin conditions and Dynamic plugin config with CEL for each mechanism’s own error handling.

Operators and functions

Operator or function

Description

&&, ||, ! Logical AND, OR, NOT.
() Group expressions to control evaluation order.
==, !=, <, <=, >, >= Standard value comparison.
+ String concatenation.
in Tests whether a value is a member of a list.
contains() Returns true if the string contains the given substring.
startsWith() Returns true if the string starts with the given prefix.
endsWith() Returns true if the string ends with the given suffix.
matches() Tests the string against a RE2 regular expression. Matches any substring unless anchored with ^ and $.
size() Returns the number of elements in a list, or the number of characters in a string.
has() Returns true if the key exists in the map. Required before accessing inner keys of kong.ctx.shared or principal.metadata.
all() Returns true if all elements in the list satisfy the predicate.
exists(), map(), filter() Additional CEL comprehension macros for working with lists.
default() Wraps an expression to return a fallback value if the expression produces a runtime error. Whether default() is available depends on the CEL-driven mechanism. See Plugin conditions and Dynamic plugin config with CEL for each mechanism’s own rules.

Regular expressions follow the rules of Rust Crate regex. Regular expression matches succeed if they match a substring of the argument. Use explicit anchors (^ and $) in the pattern to force full-string matching, if desired. For example, http.path.matches("^/api/v[0-9]+$") matches /api/v1 but not /other/api/v1.

Types

Type

Description

Example literal

bool Boolean value. true, false
int 64-bit signed integer. 42, -1
string UTF-8 string. "hello"
list<string> Ordered list of string values. ["foo", "bar"]
map<string, _type> Map with string keys and values of any type. Used for kong.ctx.shared and principal.metadata. See kong.ctx.shared field.
null Null value, returned when an optional field has no value. null

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!