Docs

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: HIT and X-AI-Cache-Age. Streaming requests are bypassed (X-AI-Cache-Status: BYPASS).
  • 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 direction option (input, output, or both) selects whether the plugin scans the request prompt, the LLM response (including streaming responses), or both. Flagged traffic is blocked with a configurable deny_code, or only logged when action is set to alert.
  • AI Aliyun Content Moderation (opens in Plugin Hub docs)
    • Added request_check_mode (last or all, default last) to control how much of a multi-turn conversation is moderated: last checks only the final user turn, while all checks every user turn. Only user-role content is moderated; system and assistant content is ignored. Long content is now split into chunks in linear time and is multi-byte (UTF-8) safe.
  • AI Proxy (opens in Plugin Hub docs)
    • The structured llm_summary object emitted to logger plugins (when logging.summaries is enabled) now includes additional AI observability fields: stream, tool_count, has_tool_calls, end_user_id, cache_read_input_tokens, cache_creation_input_tokens, and reasoning_tokens.
  • Elasticsearch Logger (opens in Plugin Hub docs)
    • Added support for authenticating to Elasticsearch with custom request headers via the headers option (for example, an Authorization: Bearer <token> or an API-key header), as an alternative to basic auth.
  • OpenID Connect (opens in Plugin Hub docs)
    • Added Redis as a session storage backend. Set session.storage to redis and configure session.redis (host, port, and related options) to store sessions in Redis instead of in the session cookie; cookie remains the default.

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 (as log_format does). A new variable, $upstream_unresolved_host, records the configured upstream host before DNS resolution. log_format_extra can be set globally through plugin metadata or per route.
  • Added per-port PROXY protocol control for the stream (L4) TCP proxy. Each stream_proxy.tcp entry 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_size configuration option (default 64 MiB) that bounds how much of a request body is read when matching post_arg.* route predicates on JSON and multipart requests. Set it to 0 to 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-gateway RPM 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.enabled configuration 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's search_path per 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 arguments were 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_choice but had no usable tools (for example, only a built-in tool that is dropped during conversion) was forwarded with the orphan tool_choice and rejected by the upstream. Such a tool_choice (and parallel_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 429 or 5xx), the error response body was discarded and the client received an empty body. The upstream error body and content type are now preserved, including across ai-proxy-multi fallback.
  • 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 count or time_window was 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.
  • 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_body was enabled and a request body exceeded max_req_body_size, the request was rejected with a misleading HTTP 401. It is now rejected with HTTP 413. The default max_req_body_size was also raised to 64 MiB (see Upgrade Notes).
  • 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_https only redirected requests whose scheme was exactly http, so a request arriving with a non-HTTP, non-HTTPS scheme (for example, via a spoofed X-Forwarded-Proto) was not redirected. It now redirects every non-HTTPS scheme.
  • Response Rewrite (opens in Plugin Hub docs)
    • Fixed issue: When the upstream response was compressed (gzip or brotli), filters were applied to the compressed bytes and did not match, producing a corrupted body. The response is now decoded before filters run.
  • 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_body enabled, 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.
  • Authz Keycloak (opens in Plugin Hub docs)
    • Fixed issue: With lazy_load_paths enabled, 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.
  • CAS Auth
    • Fixed issue: The CAS single-logout (SLO) callback POST was 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.
  • 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.username or $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 503 even 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-api7 to 0.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.