API7 Gateway 3.9.16
The 3.9.16 Helm chart raises several Data Plane shared memory (lua_shared_dict) defaults, so a Helm-deployed gateway reserves about 365 MiB more shared memory at startup than the previous chart:
Release Date: 2026-07-10
Upgrade Notes
Upgrade note — higher default gateway memory usage (Helm)
The 3.9.16 Helm chart raises several Data Plane shared memory (lua_shared_dict) defaults, so a Helm-deployed gateway reserves about 365 MiB more shared memory at startup than the previous chart:
| Shared dictionary | Previous | 3.9.16 chart |
|---|---|---|
prometheus-metrics (advanced metrics) | 15 MiB | 128 MiB |
kubernetes, nacos, nacos-stream, consul (service discovery) | 20 MiB each | 64 MiB each |
tracing_buffer (SkyWalking) | 10 MiB | 32 MiB |
api-calls-for-portal | 10 MiB | 64 MiB |
This change is in the Helm chart's shared-dict values (the gateway image defaults are unchanged), so it applies to Helm-based deployments. Before upgrading the chart, raise the gateway container's memory requests and limits so the gateway is not OOM-killed. You can lower any dictionary you do not need through the chart's shared-dict values.
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.9.16 Control Plane encrypts these fields while an older 3.9.15 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) - ai-cache:
redis_password,semantic.embedding.openai.api_key,semantic.embedding.azure_openai.api_key - ai-lakera-guard:
api_key
If you use any of these plugins with the listed fields, upgrade the Data Plane to 3.9.16 promptly after the Control Plane, and avoid editing those plugins until both sides are on 3.9.16.
Upgrade note — forwarded headers are trusted only from trusted addresses
The gateway now uses apisix.trusted_addresses to decide whether client-supplied X-Forwarded-* and RFC 7239 Forwarded headers are trusted. When trusted_addresses is not configured, or when the request comes from an untrusted address, the gateway overwrites X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Port with gateway-observed values and clears the Forwarded header before proxying upstream. If an upstream application depends on the original forwarded protocol, host, or port sent by a trusted load balancer or reverse proxy, configure that proxy's IP or CIDR in trusted_addresses.
Upgrade note — OpenID Connect silent re-authentication is no longer enabled by default
The openid-connect plugin no longer sets refresh_session_interval to 900 seconds by default. Periodic silent re-authentication now happens only when refresh_session_interval is explicitly configured. If your deployment relied on the previous 900-second refresh behavior, set refresh_session_interval: 900 before or during the upgrade.
Upgrade note — built-in user login policy is stricter
Built-in Dashboard users are now temporarily locked out after repeated failed password attempts. The default policy is enabled, bans the user and source IP after 5 consecutive failures, and lasts 15 minutes. Administrators can tune or disable it through the new login failure restriction system setting. New passwords and passwords changed after upgrade must also be at least 12 characters and still satisfy the existing complexity requirements. Existing passwords are not revalidated at sign-in.
If a built-in user enables two-factor authentication, HTTP Basic Auth for that user is rejected because Basic Auth cannot provide a second factor. Use access tokens for programmatic integrations instead; token authentication is sent with the X-API-KEY header and does not require a 2FA verification code.
Upgrade note — SQL Server deployments enable snapshot reads on first startup
For SQL Server-backed deployments, the Control Plane now creates and prepares the database before other components connect, and enables READ_COMMITTED_SNAPSHOT to prevent gateway configuration reads from blocking behind writes. On an existing SQL Server database where this setting is still disabled, the first startup applies the database-level change with ROLLBACK IMMEDIATE; in-flight database transactions and sessions can be disconnected once, after which connection pools reconnect and later starts are no-ops.
Features
Plugins
- AI Cache (opens in Plugin Hub docs) (new plugin)
- Caches LLM responses in Redis so repeated prompts can be served without calling the upstream model again. It supports exact-match caching, optional semantic (L2) matching through embeddings and RediSearch, streaming response caching and replay, configurable bypass rules, and Prometheus metrics for cache hits, misses, bypasses, and embedding latency.
- 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 request prompts, LLM responses (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_rolesto choose which request roles are moderated (user,tool, and/orsystem).userandtoolcontent followrequest_check_mode(lastorall, defaultlast), whilesystemcontent is checked on every request when selected. Long content is now split into chunks in linear time and is multi-byte (UTF-8) safe.
- Added
- AI AWS Content Moderation (opens in Plugin Hub docs)
- Request moderation now runs after AI protocol detection and checks the decoded prompt content that the upstream LLM sees, rather than the raw HTTP JSON envelope. Deny responses are returned in a provider-compatible format, with configurable
check_request,deny_code, anddeny_message.
- Request moderation now runs after AI protocol detection and checks the decoded prompt content that the upstream LLM sees, rather than the raw HTTP JSON envelope. Deny responses are returned in a provider-compatible format, with configurable
- 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 API-key header, as an alternative or complement to basicauth.
- Added support for authenticating to Elasticsearch with custom request headers via the
- Rate-limiting plugins
- Added Redis connection keepalive settings (
redis_keepalive_timeoutandredis_keepalive_pool) for the Redis and Redis Cluster policies in Limit Count (opens in Plugin Hub docs), Limit Count Advanced (opens in Plugin Hub docs), GraphQL Limit Count (opens in Plugin Hub docs), and AI Rate Limiting (opens in Plugin Hub docs), so operators can tune idle timeout and connection pool size.
- Added Redis connection keepalive settings (
Data Plane
- Added
apisix.trusted_addresses, which controls whether the gateway trusts client-suppliedX-Forwarded-*andForwardedheaders based on the resolved client address. - Added
apisix.match_uri_encoded_slash. When enabled, an encoded slash (%2F) stays encoded during route matching so it can be treated as part of a path parameter instead of a path separator. - Added
apisix.max_post_args_readable_size(default 64 MiB), which bounds how much JSON or multipart request body data is read when matchingpost_arg.*route predicates. Set it to0to disable the limit. - In standalone YAML mode, environment-variable placeholders are now resolved before YAML parsing. Unquoted placeholders can become native booleans or numbers, while quoted placeholders stay strings, preserving large IDs and token values exactly.
Control Plane
- Added TOTP two-factor authentication for built-in Dashboard users. Users can enroll, enable, disable, and recover 2FA from their account settings, and administrators can reset a user's 2FA state.
- Added login failure restriction for built-in users. Repeated failed logins temporarily lock the user and source IP, emit an audit event, and return a clear lockout message.
- Added the Dashboard UI for two-factor authentication: account setup with QR code and recovery codes, an OTP step during login, and an administrator action to reset a user's 2FA state.
- Updated password forms and generated passwords to use the new 12-character minimum length.
Fixes
Plugins
- AI Proxy (opens in Plugin Hub docs) and AI Proxy Multi (opens in Plugin Hub docs)
- Fixed issue: Anthropic Messages requests that contained tool results mixed with other content could be converted into an invalid OpenAI Chat message order, causing OpenAI-compatible upstreams to reject every later request in the session. Tool messages are now emitted immediately after the assistant tool-call message, and adjacent text or media is preserved in a following user message.
- Fixed issue: Several Anthropic-to-OpenAI conversion details diverged from LiteLLM-compatible behavior, including tool name sanitization, long tool-name collision handling, adaptive thinking effort, structured-output schema extraction, empty array encoding, and content block shaping.
- Fixed issue: Structured chat content could reach downstream AI plugins as a table and cause request processing errors. Protocol adapters now flatten text content consistently before AI guard and cache plugins consume it.
- Fixed issue: A failure while constructing the
ai-proxy-multiworking instance pool could raise a Lua error and destructively clear pool state. The failure path is now nil-safe and non-destructive.
- gRPC Transcode (opens in Plugin Hub docs)
- Fixed issue: Empty protobuf
repeatedfields were encoded as{}instead of JSON arrays ([]). Empty repeated fields now appear as arrays, including nested and descriptor-set based messages.
- Fixed issue: Empty protobuf
- Key Auth (opens in Plugin Hub docs) and other consumer authentication plugins
- Fixed issue: If a consumer credential referenced a secret that could not be resolved, the Data Plane could still index the unresolved literal value and authenticate requests with it. Consumer authentication now fails closed when a referenced secret cannot be resolved.
- Secret references
- Fixed issue: Updates or deletions of
/secretsconfiguration did not invalidate the secret LRU cache, so old secret values could continue to be used until cache expiry or indefinitely. Secret references are now re-resolved after secret configuration changes.
- Fixed issue: Updates or deletions of
- Proxy Rewrite (opens in Plugin Hub docs)
- Fixed issue: With
use_real_request_uri_unsafeanduriconfigured together, the request query string was dropped during URI rewriting. The original query string is now preserved and correctly merged when the rewritten URI already contains a query.
- Fixed issue: With
- Loggly
- Fixed issue: Batched Loggly entries from different routes could use the wrong token or tags because the async handler reused the latest route configuration. Each batch processor now keeps its own route configuration.
- Datadog (opens in Plugin Hub docs)
- Fixed issue: Large coalesced DogStatsD datagrams could exceed the common 8192-byte agent buffer and be silently truncated. The plugin now coalesces only when the payload fits, otherwise it falls back to one datagram per metric.
- Zipkin (opens in Plugin Hub docs)
- Fixed issue: Requests explicitly marked as unsampled still built full span tag tables and access-phase child spans. Unsampled requests now skip that extra tracing work while preserving trace propagation.
- OpenTelemetry (opens in Plugin Hub docs)
- Fixed issue: Updating OpenTelemetry plugin metadata at runtime did not rebuild the tracer used for injected core spans, so those spans could keep using stale collector or resource settings until worker recycle. The tracer now refreshes when metadata changes.
- Fixed issue:
additional_attributeswere evaluated before log-phase variables were populated, so attributes that depend on final request state could be missing or stale. They are now evaluated in the log phase.
- OpenID Connect (opens in Plugin Hub docs)
- Fixed issue:
refresh_session_intervalincorrectly defaulted to900, enabling silent re-authentication even when users did not configure it. The default is removed (see Upgrade Notes).
- Fixed issue:
- MQTT Proxy (opens in Plugin Hub docs)
- Fixed issue:
protocol_namewas required even though the standard default isMQTT. It is now optional and defaults toMQTT.
- Fixed issue:
- Forward Auth (opens in Plugin Hub docs)
- Fixed issue: When
request_methodwasPOST, the authorization request could forward the client'sTransfer-Encoding,Content-Length, andExpectheaders after the body had already been buffered, producing inconsistent request framing. These client framing headers are no longer copied to the auth service.
- Fixed issue: When
- Authz CASBIN
- Fixed issue: Routes using different Casbin model or policy shapes could trigger
casbin enforce error/invalid request sizeafter switching between them. The bundledlua-casbindependency is updated to include the enforcement fixes.
- Fixed issue: Routes using different Casbin model or policy shapes could trigger
- AI Aliyun Content Moderation (opens in Plugin Hub docs)
- Fixed issue: Returning
ngx.OKfrom the body filter could interrupt later body-filter processing. The plugin now returns normally so other filters can continue.
- Fixed issue: Returning
- Request ID (opens in Plugin Hub docs)
- Fixed issue: Configuring
algorithm: range_idwithout the optional range object could returnHTTP 500. The range object now has a default value.
- Fixed issue: Configuring
- Workflow (opens in Plugin Hub docs)
- Fixed issue: Workflow action plugins could run
_meta.pre_functionhooks before workflow skipped or executed the action, changing behavior even for actions that should not run. Workflow now decides whether to run the action before executing those meta hooks.
- Fixed issue: Workflow action plugins could run
- 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 through a forgedX-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. The number of sub-responses now always matches the number of sub-requests.
- 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 uploaded logs when sampling allows it.
- Fixed issue: With
- 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.
- 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 could fail to match the intended resource. 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: Log rotation could leave some log files open after only part of the file set was rotated. The gateway now reopens logs correctly after partial rotation.
- 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: 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: 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: Gateway instances could be marked OutOfSync for a short window immediately after a configuration revision changed, before the next heartbeat reported the new revision. A grace period now keeps recently heartbeating instances Healthy during that normal synchronization window.
- Fixed issue: Compatibility reports were capped at 200 unordered items, so large reports could hide errors and differ between gateway instances. The Control Plane now stores the full sorted report and computes compatibility from all items.
- Fixed issue: A secret provider request body could match a different provider type than the provider named in the URL path, causing the Data Plane to discard or misread the stored secret. The Control Plane now validates the body against the selected provider type.
- Fixed issue: The Control Plane accepted some core resource configurations that the Data Plane silently discarded, including invalid TLS, filter, and IP match configurations. These are now rejected by validation before publishing.
- Fixed issue: IPv6 upstream node hosts were rejected by the OpenAPI schema. IPv6 hosts are now accepted.
- Fixed issue: Batch
sslsvalidation used the SNI schema instead of the SSL schema, so entries without required certificate fields could pass validation. Batch SSL validation now uses the correct schema. - Fixed issue: Audit log export returned only the first 256 rows. Exports now include all matching audit logs.
- Fixed issue: Credential lookup indexes could be dropped by components that do not run database migration, and some startup schema repairs rebuilt current indexes unnecessarily. Schema repair now keeps current indexes and repairs only stale shapes.
- Fixed issue: Heartbeat and instance status queries could become slow on large deployments. Additional indexes and Go-side status computation improve these lookups.