Docs

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 400 and basic-auth plugin: password: String length must be greater than or equal to 1. On 3.9.19 the same request returned HTTP 200 and 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 with HTTP 401 and 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_resp enables response reporting, config.resp_body_size caps how much of the response body is reported in KB (0 reports headers only), and config.extra_ignored_content_types adds 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.
  • AI Proxy Multi (opens in Plugin Hub docs)
    • Instance health checks are now reported through the Data Plane control API. GET /v1/healthcheck lists one entry per instance that declares checks, carrying the new plugin and meta fields (meta.instance names the instance), and a new sub-resource GET /v1/healthcheck/{src_type}/{src_id}/checkers returns every checker a resource owns as an array. A route whose real upstreams were LLM instances previously reported nothing, and GET /v1/healthcheck/{src_type}/{src_id} answered HTTP 404 with no checker for routes[1].

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_issues returns one entry per distinct problem — a machine-readable reason (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[*].weight covers them all. Dismissals can be managed from the Dashboard or through the /api/compatibility_dismiss_rules endpoints, 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 search parameter on the service list endpoint matches, case-insensitively, any substring of a service's hosts or path_prefix in 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 with HTTP 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 config block reset every option it did not set, discarding the values configured in the plugin metadata. A route that set only read_timeout silently reverted req_body_size, connect_timeout and the rest to the built-in defaults. A plugin-level config now overrides the metadata field by field, as the documentation already described.
  • AI Cache (opens in Plugin Hub docs)
    • Fixed issue: Under the passthrough protocol, requests with the same body but a different method, path or query string shared one cache entry, so a request to /v1/images/generations on 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.
  • 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 model contained a /, a ? or a space was sent to the wrong path or failed outright. A model named models/text-embedding-004 added a path segment to the prediction URL instead of naming the model, and a ? or a space produced HTTP 500 with invalid characters found in path in the error log — the request was never sent. The model is now escaped into a single path segment.
  • Traffic Label (opens in Plugin Hub docs)
    • Fixed issue: On a route carrying an ipmatch rule, 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/routes on the Data Plane control API failed, route dumps at info log level came out empty, and matching requests logged failed to encode: Cannot serialise table: excessively sparse array.
  • AWS Lambda (opens in Plugin Hub docs), Azure Functions and OpenFunction
    • Fixed issue: A function_uri with no path — https://xxx.lambda-url.us-east-1.on.aws with no trailing slash, for example — produced a request with an empty request target, which a strict function endpoint rejected with HTTP 400 Bad Request. The request is now sent to /. A function response with status 400 or above is also logged at warn with its status and body; previously the error log carried only exits with http status code 403, with no indication of why.

Data Plane

  • Fixed issue: On the Data Plane control API, GET /v1/healthcheck returned an empty JSON object where an array was expected — "nodes": {} for an upstream configured with checks but 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 between http and stream.
  • Fixed issue: When Prometheus answered the Control Plane with something other than JSON — an authenticating proxy returning an HTML 401 page, for example — the Dashboard's monitoring pages and the Developer Portal's metrics endpoint failed with HTTP 500 and invalid character '<' looking for beginning of value, which said nothing about the real cause. Such a reply is now reported as HTTP 502 with a message naming the upstream status, for example prometheus 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.