When an ASM gateway or sidecar proxy intercepts a request and returns an HTTP response directly -- without forwarding it to the upstream service -- the default response body is a generic message such as not found or RBAC: Access Denied. The CustomLocalReply plug-in replaces these default responses with custom status codes, headers, and body content, so you can redirect stale URLs, brand error pages, or standardize API error formats across your mesh.
Scenarios
| If you want to... | See |
|---|---|
| Redirect outdated URLs to a new location | Redirect a 404 to another URL |
| Return a branded HTML error page instead of plain text | Return a custom 403 page |
| Standardize API error responses in JSON format | Return a JSON error response |
When local responses are generated
ASM generates a local response (bypassing the upstream service) in the following situations:
| Trigger | Default status code | Default body |
|---|---|---|
| No routing rule matches the request | 404 | not found |
| An authorization policy rejects the request | 403 | RBAC: Access Denied |
A directResponse is configured in the virtual service | User-defined | User-defined |
The CustomLocalReply plug-in intercepts these responses before they reach the downstream service and applies your overrides.
Prerequisites
Before you begin, make sure that you have:
An ingress gateway is deployed. For more information, see Create an ingress gateway.
The HTTPBin service running in the data plane cluster. See Deploy the HTTPBin application
Configuration reference
Top-level fields
| Field | Type | Required | Valid values | Description |
|---|---|---|---|---|
patch_context | String | Yes | GATEWAY, SIDECAR_INBOUND | Execution context. Set to GATEWAY for an ASM gateway, or SIDECAR_INBOUND for a sidecar proxy. |
custom_error_pages | CustomErrorPage[] | Yes | -- | List of error-page overrides. Each entry maps a locally generated status code to a custom response. |
CustomErrorPage fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
match_status_code | Integer | Yes | -- | The locally generated status code to intercept. When the gateway or sidecar generates this code, the override below is applied. |
return_status_code | Integer | Yes | -- | The status code returned to the downstream service in place of the matched code. |
content_type | String | Yes | -- | The content-type response header value (for example, text/html; charset=UTF-8 or application/json). |
headers | Map[string]string | No | null | Additional response headers to include in the custom response. |
body | String | Yes | -- | The response body returned to the downstream service. |
Redirect a 404 to another URL
This example intercepts the 404 response generated by a directResponse rule and turns it into a 301 redirect.
Step 1: Deploy a VirtualService with a direct response
Apply the following VirtualService to your ASM instance. It configures the gateway to return a 404 status code with the body not found for all requests.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: httpbin
namespace: default
spec:
gateways:
- httpbin-gateway
hosts:
- '*'
http:
- directResponse:
body:
string: not found
status: 404Step 2: Enable the CustomLocalReply plug-in
Apply the following plug-in configuration to the ASM ingress gateway named ingressgateway. It matches the 404 status code and replaces it with a 301 redirect to https://www.aliyun.com.
patch_context: GATEWAY
custom_error_pages:
- match_status_code: 404
return_status_code: 301
headers:
location: 'https://www.aliyun.com'
content_type: text/html; charset=UTF-8
body: movedVerify the redirect
Open a browser and navigate to the ASM gateway IP address. The browser redirects to https://www.aliyun.com, confirming that the plug-in configuration is active.
Return a custom 403 page
This example replaces the default RBAC: Access Denied message with a branded HTML error page when an authorization policy rejects a request.
Apply the following plug-in configuration to the ASM ingress gateway named ingressgateway:
patch_context: GATEWAY
custom_error_pages:
- match_status_code: 403
return_status_code: 403
content_type: text/html; charset=UTF-8
body: |
<!DOCTYPE html>
<html>
<head><title>Access Denied</title></head>
<body>
<h1>403 - Access Denied</h1>
<p>You do not have permission to access this resource.
Contact your administrator if you believe this is an error.</p>
</body>
</html>When an authorization policy rejects a request, the gateway returns this HTML page instead of the plain-text RBAC: Access Denied message.
Return a JSON error response
This example returns a structured JSON error body for 404 responses, which is useful for API services that require a consistent error format.
Apply the following plug-in configuration to the ASM ingress gateway named ingressgateway:
patch_context: GATEWAY
custom_error_pages:
- match_status_code: 404
return_status_code: 404
content_type: application/json
body: |
{
"error": {
"code": 404,
"message": "The requested resource was not found.",
"status": "NOT_FOUND"
}
}When the gateway generates a 404 response, the downstream service receives a JSON body with a standardized error structure instead of the plain-text not found message.
Multiple overrides in a single configuration
Define multiple entries in custom_error_pages to handle different status codes in a single plug-in configuration. The following example overrides both 404 and 403 responses:
patch_context: GATEWAY
custom_error_pages:
- match_status_code: 404
return_status_code: 404
content_type: application/json
body: |
{
"error": {
"code": 404,
"message": "The requested resource was not found.",
"status": "NOT_FOUND"
}
}
- match_status_code: 403
return_status_code: 403
content_type: application/json
body: |
{
"error": {
"code": 403,
"message": "Access denied by authorization policy.",
"status": "FORBIDDEN"
}
}