Description
Description
The error-page plugin currently has two limitations that make custom error pages less useful in production:
1. Limited status code coverage. Only 404/500/502/503 are supported. APISIX-generated 403 responses are common — auth plugin rejections (key-auth, jwt-auth, etc.), ACL blocks, or custom serverless logic — but they cannot be replaced with a custom page.
2. Static body only. The configured body is returned as a static string. Operators typically want to embed request-scoped information into error pages — most importantly the request ID, so that end users can report it and administrators can correlate the error page with access/error logs.
Proposal
Part 1 — Support 403:
- Add
error_403 to metadata_schema (same structure as existing entries)
- Lower the status threshold in
get_metadata from status < 404 to status < 400; interception still requires an explicitly configured error_<status> key, so behavior for unconfigured codes is unchanged
Part 2 — Nginx variables in body:
- Resolve variables via
core.utils.resolve_var (the same mechanism used by response-rewrite headers) before sending the response
- Resolution happens in
header_filter so Content-Length is computed from the final rendered body (avoids truncation/connection stalls)
- Supports
$var, ${var}, the ?? default-value operator, and \$ escaping
- Fail-open: on resolution failure, fall back to the original body and log a warning
- Zero overhead for existing configurations (bodies without
$ short-circuit before regex processing)
Example:
{
"enable": true,
"error_403": {
"body": "<html><body><h1>403 Forbidden</h1><p>Request ID: $apisix_request_id</p></body></html>",
"content_type": "text/html"
}
}
Both parts are backward compatible and independent of each other, but they combine naturally — e.g. a custom 403 page showing $apisix_request_id for troubleshooting auth rejections.
Description
Description
The
error-pageplugin currently has two limitations that make custom error pages less useful in production:1. Limited status code coverage. Only 404/500/502/503 are supported. APISIX-generated 403 responses are common — auth plugin rejections (key-auth, jwt-auth, etc.), ACL blocks, or custom serverless logic — but they cannot be replaced with a custom page.
2. Static body only. The configured
bodyis returned as a static string. Operators typically want to embed request-scoped information into error pages — most importantly the request ID, so that end users can report it and administrators can correlate the error page with access/error logs.Proposal
Part 1 — Support 403:
error_403tometadata_schema(same structure as existing entries)get_metadatafromstatus < 404tostatus < 400; interception still requires an explicitly configurederror_<status>key, so behavior for unconfigured codes is unchangedPart 2 — Nginx variables in body:
core.utils.resolve_var(the same mechanism used byresponse-rewriteheaders) before sending the responseheader_filtersoContent-Lengthis computed from the final rendered body (avoids truncation/connection stalls)$var,${var}, the??default-value operator, and\$escaping$short-circuit before regex processing)Example:
{ "enable": true, "error_403": { "body": "<html><body><h1>403 Forbidden</h1><p>Request ID: $apisix_request_id</p></body></html>", "content_type": "text/html" } }Both parts are backward compatible and independent of each other, but they combine naturally — e.g. a custom 403 page showing
$apisix_request_idfor troubleshooting auth rejections.