API7 Gateway 3.9.20
Basic Auth — A basic auth password can no longer be empty.
Release Date: 2026-09-03
Breaking Changes
Plugins
-
Basic Auth (opens in Plugin Hub docs)
Upgrade note
A basic auth password can no longer be empty. The Control Plane now rejects a consumer or a credential whose password is an empty string with
HTTP 400andbasic-auth plugin: password: String length must be greater than or equal to 1. On 3.9.19 the same request returnedHTTP 200and was stored.A consumer with an empty password that is already stored turns into an error-level entry in the configuration compatibility report after the upgrade, and any further edit of it is rejected until the password is set.
The Data Plane now fails closed as well: a password that resolves to an empty string, including an
$env://or$secret://reference that resolves to"", is rejected withHTTP 401and a warning in the error log. On 3.9.19 such a consumer authenticated successfully with credentials of the username, a colon, and a space.Before upgrading, check for basic auth consumers and credentials whose password is empty or whose secret reference resolves to an empty value, and give each of them a real password.
Upgrade Notes
Upgrade note — the grouped compatibility report and dismiss rules need an upgraded Data Plane
The compatibility report is now grouped by issue, and a dismiss rule matches on the machine-readable reason the Data Plane reports. Both depend on the Data Plane reporting compatibility problems as structured data, which it does from 3.9.20. API7 Enterprise is upgraded Control Plane first, so during the upgrade window a 3.9.20 Control Plane is talking to gateway instances that are still on 3.9.19, and for those instances neither works.
An older Data Plane reports each problem as a rendered English sentence carrying no machine-readable reason. The Control Plane drops such a record as the heartbeat arrives, so for as long as the Control Plane is upgraded and the gateway instance is not, that instance's compatibility report is empty. Problems that genuinely exist on it — a resource the Data Plane rejected, a plugin it does not have — are not shown, and a dismiss rule has nothing to act on. Plan for the report to be blank rather than trustworthy until the instance is upgraded, and do not read a blank report as a clean one.
The instance itself is still flagged as needing an upgrade by the version rules, so it does not silently look up to date. This affects only what the compatibility report displays. It never affects traffic or the configuration a gateway instance runs, and the report behaves as documented as soon as the instance is upgraded.
Features
Plugins
- Chaitin WAF (opens in Plugin Hub docs)
- The response can now be reported to the SafeLine detection service alongside the request, so data leaked in a response body, an exploit's output, or an unexpected status code becomes visible to SafeLine. Three options are available both on the plugin and in its plugin metadata:
config.log_respenables response reporting,config.resp_body_sizecaps how much of the response body is reported in KB (0reports headers only), andconfig.extra_ignored_content_typesadds comma-separated response content types to skip on top of the built-in list. Reporting is off by default. The report is sent after the response has been delivered to the client and is advisory only: it never blocks or modifies a response.
- The response can now be reported to the SafeLine detection service alongside the request, so data leaked in a response body, an exploit's output, or an unexpected status code becomes visible to SafeLine. Three options are available both on the plugin and in its plugin metadata:
- AI Proxy Multi (opens in Plugin Hub docs)
- Instance health checks are now reported through the Data Plane control API.
GET /v1/healthchecklists one entry per instance that declareschecks, carrying the newpluginandmetafields (meta.instancenames the instance), and a new sub-resourceGET /v1/healthcheck/{src_type}/{src_id}/checkersreturns every checker a resource owns as an array. A route whose real upstreams were LLM instances previously reported nothing, andGET /v1/healthcheck/{src_type}/{src_id}answeredHTTP 404withno checker for routes[1].
- Instance health checks are now reported through the Data Plane control API.
Control Plane
- The gateway instance configuration compatibility report is now organized by issue rather than by resource.
GET /api/gateway_groups/{gateway_group_id}/instances/{gateway_instance_id}/compatibility_issuesreturns one entry per distinct problem — a machine-readablereason(resource_invalid,plugin_unavailable,plugin_config_invalid,plugin_unknown_fields), the plugin and field it concerns, and the resources carrying it — ordered errors first, then by how many resources are affected. The Data Plane reports each issue as structured data instead of one rendered English sentence per resource, so a single cause is no longer repeated across a thousand rows and an unrecognized-field warning is no longer buried under an aggregated error. - An operator can now dismiss a plugin field the Data Plane does not recognize but safely ignores, so the compatibility report lists only what needs acting on. A dismissal names the plugin and the field and applies across every gateway group and instance, covering every element of an array — dismissing
nodes[*].weightcovers them all. Dismissals can be managed from the Dashboard or through the/api/compatibility_dismiss_rulesendpoints, are governed by their own permissions, and every change is audited. Only unrecognized-field warnings can be dismissed: dismissing one never changes what the Data Plane does with the configuration, and never turns an Incompatible instance Compatible. - Services can now be found by domain or path prefix. The
searchparameter on the service list endpoint matches, case-insensitively, any substring of a service'shostsorpath_prefixin addition to name, description, labels and ID; whitespace-separated terms keep their AND semantics. A user who knew only a service's domain or path prefix previously had no way to look it up. The path prefix is recorded in a column that this release adds, and the upgrade backfills it for every existing service, so services published before the upgrade are searchable by path prefix without being republished.
Console (Dashboard)
- Configuration compatibility warnings now show each affected resource with its full business hierarchy path (for example, gateway group → service → route) instead of a bare resource ID, making it easier to locate the configuration that needs attention.
Fixes
Plugins
- Basic Auth (opens in Plugin Hub docs)
- Fixed issue: A consumer whose password contained a colon could never authenticate. RFC 7617 defines everything after the first colon as the password, but the credentials were split on every colon, which truncated it.
- Fixed issue: A consumer whose password was empty — stored as an empty string, or resolved from an
$env://or$secret://reference that yielded one — authenticated any request presenting its username with an empty password. Such a consumer is now rejected withHTTP 401, and an empty password can no longer be configured. See Breaking Changes for what to check before upgrading.
- Chaitin WAF (opens in Plugin Hub docs)
- Fixed issue: Setting any option in a route- or service-level
configblock reset every option it did not set, discarding the values configured in the plugin metadata. A route that set onlyread_timeoutsilently revertedreq_body_size,connect_timeoutand the rest to the built-in defaults. A plugin-levelconfignow overrides the metadata field by field, as the documentation already described.
- Fixed issue: Setting any option in a route- or service-level
- AI Cache (opens in Plugin Hub docs)
- Fixed issue: Under the
passthroughprotocol, requests with the same body but a different method, path or query string shared one cache entry, so a request to/v1/images/generationson a wildcard route could be served the cached response of an earlier request to/v1/chat/completions. The cache key for that protocol now includes the client's method, path and query. Other protocols keep their existing keys, so entries cached before the upgrade stay valid.
- Fixed issue: Under the
- AI Proxy (opens in Plugin Hub docs) and AI Proxy Multi (opens in Plugin Hub docs)
- Fixed issue: With the Vertex AI provider, an embeddings request whose
modelcontained a/, a?or a space was sent to the wrong path or failed outright. A model namedmodels/text-embedding-004added a path segment to the prediction URL instead of naming the model, and a?or a space producedHTTP 500withinvalid characters found in pathin the error log — the request was never sent. The model is now escaped into a single path segment.
- Fixed issue: With the Vertex AI provider, an embeddings request whose
- Traffic Label (opens in Plugin Hub docs)
- Fixed issue: On a route carrying an
ipmatchrule, the compiled matcher was stored inside the plugin's own configuration, so from the first matching request onward the route could no longer be serialized to JSON.GET /v1/routeson the Data Plane control API failed, route dumps atinfolog level came out empty, and matching requests loggedfailed to encode: Cannot serialise table: excessively sparse array.
- Fixed issue: On a route carrying an
- AWS Lambda (opens in Plugin Hub docs), Azure Functions and OpenFunction
- Fixed issue: A
function_uriwith no path —https://xxx.lambda-url.us-east-1.on.awswith no trailing slash, for example — produced a request with an empty request target, which a strict function endpoint rejected withHTTP 400 Bad Request. The request is now sent to/. A function response with status400or above is also logged atwarnwith its status and body; previously the error log carried onlyexits with http status code 403, with no indication of why.
- Fixed issue: A
Data Plane
- Fixed issue: On the Data Plane control API,
GET /v1/healthcheckreturned an empty JSON object where an array was expected —"nodes": {}for an upstream configured withchecksbut not yet used, and{}for the whole result when nothing was being health checked. Both are now[].
Control Plane
- Fixed issue: A gateway instance was looked up without its gateway group and without ordering its runs, so an instance that had been restarted was answered for from a run that no longer exists, for up to seven days. The compatibility issues endpoint returned a report contradicting the summary shown beside it on the same screen, and never converged; deleting a gateway instance could clear the offline guard on the strength of a stale run and remove a gateway that was still connected, still answering
HTTP 200; and health check status reported by the Data Plane could be filed under the wrong gateway group when the same instance ID existed in two groups. - Fixed issue: A service's type was recorded for filtering only when the service was created, never when it was updated, so
?type=filtering kept the value the service had at creation even after the service was moved betweenhttpandstream. - Fixed issue: When Prometheus answered the Control Plane with something other than JSON — an authenticating proxy returning an HTML
401page, for example — the Dashboard's monitoring pages and the Developer Portal's metrics endpoint failed withHTTP 500andinvalid character '<' looking for beginning of value, which said nothing about the real cause. Such a reply is now reported asHTTP 502with a message naming the upstream status, for exampleprometheus returned 401 Unauthorized, and the first 256 bytes of the body go to the Control Plane log instead of to the client. Prometheus's own JSON error responses still pass through unchanged.