API7 Gateway 3.10.2
The Control Plane now encrypts additional credential-bearing plugin fields at rest.
Release Date: 2026-06-29
Upgrade Notes
Upgrade note — additional plugin secret fields are encrypted at rest
The Control Plane now encrypts additional credential-bearing plugin fields at rest. Because API7 EE upgrades the Control Plane before the Data Plane, during the upgrade window a 3.10.2 Control Plane encrypts these fields while an older 3.10.1 Data Plane cannot decrypt them, which can cause the affected plugins to fail until the Data Plane is also upgraded.
The newly encrypted fields, by plugin, are:
- limit-count, limit-count-advanced, and graphql-limit-count:
redis_password,sentinel_password - limit-conn:
redis_password - limit-req:
redis_password - ai-rate-limiting:
redis_password,sentinel_password - elasticsearch-logger:
headers(the custom authentication headers) - openid-connect:
session.redis.password - ai-cache:
redis_password - ai-lakera-guard:
api_key
If you use any of these plugins with the listed fields, upgrade the Data Plane to 3.10.2 promptly after the Control Plane, and avoid editing those plugins until both sides are on 3.10.2.
Upgrade note — hmac-auth default request body limit raised to 64 MiB
When hmac-auth has validate_request_body enabled, the default max_req_body_size is now 67108864 bytes (64 MiB), aligned with Apache APISIX. In 3.10.1 this default was 524288 bytes (512 KiB), which rejected request bodies between 512 KiB and 64 MiB with HTTP 413. After upgrading, such requests are accepted by default. If you relied on the lower limit, set max_req_body_size explicitly to restore it.
Upgrade note — Developer Portal signup consent configuration changed
The Developer Portal signup consent is now configured with a single signUpConsentLabel option (an HTML snippet rendered next to the consent checkbox), and the consent check is enforced only when a label is configured. The previous tosURL and beforeSignUpButtonHtml options have been removed. If your portal configuration sets either of those keys, move the content to signUpConsentLabel before upgrading; otherwise the signup consent text will no longer be shown.
Features
Plugins
- AI Cache (opens in Plugin Hub docs) (new plugin)
- Caches LLM responses so that identical requests are served from a cache instead of calling the upstream model again. Exact-match caching is keyed on the normalized request body and stored in Redis; a cache hit returns the stored response with the headers
X-AI-Cache-Status: HITandX-AI-Cache-Age. Streaming requests are bypassed (X-AI-Cache-Status: BYPASS).
- Caches LLM responses so that identical requests are served from a cache instead of calling the upstream model again. Exact-match caching is keyed on the normalized request body and stored in Redis; a cache hit returns the stored response with the headers
- AI Lakera Guard (opens in Plugin Hub docs) (new plugin)
- Screens AI traffic through the Lakera Guard API to detect prompt injection and other unsafe content. The
directionoption (input,output, orboth) selects whether the plugin scans the request prompt, the LLM response (including streaming responses), or both. Flagged traffic is blocked with a configurabledeny_code, or only logged whenactionis set toalert.
- Screens AI traffic through the Lakera Guard API to detect prompt injection and other unsafe content. The
- AI Aliyun Content Moderation (opens in Plugin Hub docs)
- Added
request_check_mode(lastorall, defaultlast) to control how much of a multi-turn conversation is moderated:lastchecks only the final user turn, whileallchecks every user turn. Onlyuser-role content is moderated;systemandassistantcontent is ignored. Long content is now split into chunks in linear time and is multi-byte (UTF-8) safe.
- Added
- AI Proxy (opens in Plugin Hub docs)
- The structured
llm_summaryobject emitted to logger plugins (whenlogging.summariesis enabled) now includes additional AI observability fields:stream,tool_count,has_tool_calls,end_user_id,cache_read_input_tokens,cache_creation_input_tokens, andreasoning_tokens.
- The structured
- Elasticsearch Logger (opens in Plugin Hub docs)
- Added support for authenticating to Elasticsearch with custom request headers via the
headersoption (for example, anAuthorization: Bearer <token>or an API-key header), as an alternative to basicauth.
- Added support for authenticating to Elasticsearch with custom request headers via the
- OpenID Connect (opens in Plugin Hub docs)
- Added Redis as a session storage backend. Set
session.storagetoredisand configuresession.redis(host, port, and related options) to store sessions in Redis instead of in the session cookie;cookieremains the default.
- Added Redis as a session storage backend. Set
Data Plane
- Added
log_format_extra, an additive log format for logger plugins that adds fields to the default rich log format instead of replacing it (aslog_formatdoes). A new variable,$upstream_unresolved_host, records the configured upstream host before DNS resolution.log_format_extracan be set globally through plugin metadata or per route. - Added per-port PROXY protocol control for the stream (L4) TCP proxy. Each
stream_proxy.tcpentry can independently enable receiving the PROXY protocol (proxy_protocol) and sending it to the upstream (proxy_protocol_to_upstream), overriding the global defaults. - Added the
max_post_args_readable_sizeconfiguration option (default 64 MiB) that bounds how much of a request body is read when matchingpost_arg.*route predicates on JSON and multipart requests. Set it to0to disable the limit. - Debug sessions now capture each request's logs as OpenTelemetry span events on the request's root span, so per-request logs are available in the trace without an external log collector.
Control Plane
- Added an RPM install method for the Data Plane, alongside the existing Docker and Helm options. The Dashboard's gateway-group deployment page has a new RPM tab; after the
api7-gatewayRPM is installed on an air-gapped host, the generated offline script provisions the gateway-group client certificate, writes the gateway configuration, and joins the instance to the Control Plane.
Developer Portal
- Disable the API Hub: operators can turn the API Hub off entirely with the
apiHub.enabledconfiguration flag. When disabled, the API Hub link is hidden from the navigation, API Hub pages return not-found, and API Hub URLs are removed from the sitemap. - Require email verification: signup and sign-in can be configured to require developers to verify their email address before authentication completes.
- Custom PostgreSQL schema: the portal can be deployed into a custom PostgreSQL schema instead of
public, applying the schema'ssearch_pathper connection and running schema-scoped migrations. - Organization management for platform admins: the admin Organizations page can now take over an organization (becoming its owner) or delete an organization, in addition to the existing user-management actions.
- Documentation Markdown and LLM endpoints: the in-portal documentation site now exposes Markdown and LLM-friendly text endpoints for use by AI tooling, and individual documentation pages can be excluded from those endpoints while remaining readable in the docs UI.
Fixes
Plugins
- AI Proxy (opens in Plugin Hub docs)
- Fixed issue: When the upstream returned a tool call whose
argumentswere not valid JSON, the entire response conversion was aborted and the client received nothing. The malformed tool call now falls back to an empty argument object, and the rest of the response (including any text content) is preserved. - Fixed issue: A request that carried
tool_choicebut had no usabletools(for example, only a built-in tool that is dropped during conversion) was forwarded with the orphantool_choiceand rejected by the upstream. Such atool_choice(andparallel_tool_calls) is now removed. Separately, a streaming Anthropic request whose upstream omitted the final completion chunk no longer hangs the client until timeout — the stream is now terminated correctly. - Fixed issue: When the upstream LLM returned an error status (such as
HTTP 429or5xx), the error response body was discarded and the client received an empty body. The upstream error body and content type are now preserved, including acrossai-proxy-multifallback.
- Fixed issue: When the upstream returned a tool call whose
- AI Proxy Multi (opens in Plugin Hub docs)
- Fixed issue: A failure while constructing the working instance pool could raise a Lua error and destructively clear pool state. The failure path is now nil-safe and non-destructive.
- Limit Count (opens in Plugin Hub docs)
- Fixed issue: When
countortime_windowwas resolved from a variable, an invalid value (non-integer, zero or negative, or beyond the safe integer range) was silently ignored, which could disable the rate limit entirely. Such values are now validated and rejected, closing a rate-limit bypass. - Fixed issue: With the Redis policy and sliding-window counting, the check-and-increment was not atomic, so concurrent requests could exceed the configured limit. Counting is now performed atomically with a Redis script.
- Fixed issue: When
- Limit Request (opens in Plugin Hub docs)
- Fixed issue: The rate-limit counter was keyed so that a limit attached to a shared resource (such as a Consumer) counted each route separately instead of sharing one bucket. The counter is now keyed by the parent resource, so a Consumer-level limit is enforced across all of the Consumer's routes.
- HMAC Auth (opens in Plugin Hub docs)
- Fixed issue: When
validate_request_bodywas enabled and a request body exceededmax_req_body_size, the request was rejected with a misleadingHTTP 401. It is now rejected withHTTP 413. The defaultmax_req_body_sizewas also raised to 64 MiB (see Upgrade Notes).
- Fixed issue: When
- Attach Consumer Label (opens in Plugin Hub docs)
- Fixed issue: A client could spoof a configured header when the matched Consumer had no labels, because the plugin overwrote the header only when a label value was present. Configured headers are now always stripped from the client request, even when the Consumer has no matching label.
- Redirect
- Fixed issue:
http_to_httpsonly redirected requests whose scheme was exactlyhttp, so a request arriving with a non-HTTP, non-HTTPS scheme (for example, via a spoofedX-Forwarded-Proto) was not redirected. It now redirects every non-HTTPS scheme.
- Fixed issue:
- Response Rewrite (opens in Plugin Hub docs)
- Fixed issue: When the upstream response was compressed (gzip or brotli),
filterswere applied to the compressed bytes and did not match, producing a corrupted body. The response is now decoded before filters run.
- Fixed issue: When the upstream response was compressed (gzip or brotli),
- Batch Requests
- Fixed issue: When a pipelined sub-request timed out, the response array could contain more entries than there were sub-requests (an extra empty object). The number of sub-responses now always matches the number of sub-requests.
- Loki Logger (opens in Plugin Hub docs)
- Fixed issue: Log labels resolved from variables were written back to the shared plugin configuration, so the first request's values were frozen and reused for all subsequent requests. Labels are now resolved per request.
- Tencent Cloud CLS
- Fixed issue: With
include_req_bodyenabled, the request body was not captured because it was not read in the access phase. The body is now read so that it is included in the uploaded logs.
- Fixed issue: With
- Authz Keycloak (opens in Plugin Hub docs)
- Fixed issue: With
lazy_load_pathsenabled, the request query string was included when resolving the Keycloak resource by URI, so requests with query parameters failed to match a resource and were denied. The query string is now stripped before resolution.
- Fixed issue: With
- CAS Auth
- Fixed issue: The CAS single-logout (SLO) callback
POSTwas proxied to the upstream instead of being handled by the plugin. The callback is now terminated by the plugin and is no longer forwarded upstream.
- Fixed issue: The CAS single-logout (SLO) callback
- gRPC Web (opens in Plugin Hub docs)
- Fixed issue: A debug log statement wrote the decoded request body to the error log. The statement has been removed so request payloads are no longer leaked into logs.
Data Plane
- Fixed issue: Resolving a dotted context variable in a log format (such as
$consumer.usernameor$llm_summary.model) raised an error and dropped the log line when the parent object was absent (for example, an unauthenticated request that has no consumer). The missing value is now handled gracefully. - Fixed issue: After a transient DNS or service-discovery failure, a domain-name upstream could remain stuck returning
HTTP 503even after the name resolved again. The upstream now recovers once resolution succeeds. - Fixed issue: When a node's health changed, the consistent-hash (chash) ring was rebuilt against the healthy subset, remapping keys that belonged to healthy nodes. The ring is now kept stable so that only keys of the failed node are remapped.
- Fixed issue: Environment-variable substitution in configuration directives could match the wrong variable when one variable name was a prefix of another. Names are now resolved exactly.
- Fixed issue: Upgraded the Prometheus metrics library (
nginx-lua-prometheus-api7to0.20260623) to remove duplicate metric series that could cause a scrape to be rejected.
Control Plane
- Fixed issue: A Data Plane running the latest gateway version was marked Incompatible when its configuration report contained errors. A latest-version Data Plane now stays Compatible, and the configuration errors are still surfaced in the compatibility report summary.
Developer Portal
- Fixed issue: Enabling or disabling two-factor authentication did not actually verify the account password, the backup-codes dialog could appear empty, and entering a wrong TOTP code at sign-in navigated to the home page instead of showing an error. Password verification, backup-code display, and TOTP error handling now work correctly.
- Fixed issue: An email-domain SSO policy was enforced only in the UI, so direct calls to the authentication endpoints could bypass it. The policy is now enforced on the server, so password sign-in, magic-link, and password-reset requests are rejected for domains that are required to use SSO.