All Products
Search
Document Center

Alibaba Cloud Service Mesh:Add HTTP security response headers by using an EnvoyFilter

Last Updated:Mar 11, 2026

Web applications without security response headers are vulnerable to clickjacking, cross-site scripting (XSS), and content sniffing attacks. In Alibaba Cloud Service Mesh (ASM), you can deploy an EnvoyFilter at the ingress gateway to inject these headers into all responses automatically. This hardens your applications without modifying application code.

Important

EnvoyFilter patches the Envoy proxy configuration directly. Incorrect configurations can destabilize the entire mesh. Always test in a non-production environment first, and review your EnvoyFilter definitions after each ASM or Istio version upgrade to replace deprecated fields.

Security headers reference

The following table lists the HTTP security response headers based on Open Web Application Security Project (OWASP) recommendations. The EnvoyFilter in this topic adds these headers to all responses that pass through the ingress gateway.

HeaderDefault valuePurpose
Content-Security-Policyframe-ancestors none;Prevents clickjacking by blocking other sites from framing your pages
X-XSS-Protection1;mode=blockActivates the browser XSS filter and stops page rendering when an attack is detected
X-Content-Type-OptionsnosniffPrevents browsers from MIME-sniffing the content type
Referrer-Policyno-referrerStops the browser from sending referrer information with requests
X-Download-OptionsnoopenPrevents older versions of Internet Explorer from automatically running downloads
X-DNS-Prefetch-ControloffDisables DNS prefetching for external links
ServerenvoyAutomatically set by the Istio ingress gateway
X-Powered-ByN/ARemoved by the filter to hide application server details
Feature-Policycamera 'none';microphone 'none';geolocation 'none';encrypted-media 'none';payment 'none';speaker 'none';usb 'none';Restricts browser features and APIs available to the page
Note

The Feature-Policy header is deprecated by the W3C in favor of Permissions-Policy. The EnvoyFilter in this topic uses Feature-Policy for backward compatibility with older browsers. To adopt Permissions-Policy, modify the Lua script accordingly.

Prerequisites

Before you begin, make sure that you have:

Check existing response headers

Before deploying the EnvoyFilter, check the headers your application currently returns. This example uses the Bookinfo sample application.

Run the following command. Replace <ingress-gateway-ip> with the IP address of your ingress gateway. To find this IP address, see Use Istio resources to route traffic to different versions of a service.

curl -I http://<ingress-gateway-ip>/productpage

Expected output:

HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 5183
server: istio-envoy
date: Tue, 28 Jan 2020 08:15:21 GMT
x-envoy-upstream-service-time: 28

None of the security headers from the reference table appear in the response. The following sections show how to add them.

Deploy the EnvoyFilter

Choose one of the following methods based on your ASM instance version.

ASM 1.12.4.0 or later: use the ASM console

ASM 1.12.4.0 and later provide a built-in plug-in that creates the EnvoyFilter automatically.

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose Plugin Extension Center > Market Place.

  3. On the Market Place page, click Add HTTP response headers. On the Plugin Detail page, click Create Plug-in Instance.

  4. In the Plugin Effective scope section, select Gateway Scope and click Add ASM Gateway to effective scope.

  5. In the Add ASM Gateway to effective scope dialog box, select ingressgateway in the Select ASM Gateway section, click the add icon to move it to the selected section, and then click OK.

    Note

    ingressgateway is the default ingress gateway. Select a different gateway if you want the security headers to apply to a different gateway.

  6. In the Plugin Config section, clear all content from the YAML editor, turn on Plugin Switch, and wait for the plug-in to activate.

After the plug-in activates, the EnvoyFilter is created automatically.

Earlier than ASM 1.12.4.0: use kubectl

For ASM versions earlier than 1.12.4.0, deploy the EnvoyFilter by running the following kubectl command:

View the full EnvoyFilter YAML

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: addheader-into-ingressgateway
  namespace: istio-system
  labels:
    asm-system: 'true'
    provider: asm
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      proxy:
        proxyVersion: '^1\.9.*'
      listener:
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.router"
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.lua
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
          inlineCode: |-
            function envoy_on_response(response_handle)
                function hasFrameAncestors(rh)
                s = rh:headers():get("Content-Security-Policy");
                delimiter = ";";
                defined = false;
                for match in (s..delimiter):gmatch("(.-)"..delimiter) do
                    match = match:gsub("%s+", "");
                    if match:sub(1, 15)=="frame-ancestors" then
                    return true;
                    end
                end
                return false;
                end
                if not response_handle:headers():get("Content-Security-Policy") then
                csp = "frame-ancestors none;";
                response_handle:headers():add("Content-Security-Policy", csp);
                elseif response_handle:headers():get("Content-Security-Policy") then
                if not hasFrameAncestors(response_handle) then
                    csp = response_handle:headers():get("Content-Security-Policy");
                    csp = csp .. ";frame-ancestors none;";
                    response_handle:headers():replace("Content-Security-Policy", csp);
                end
                end
                if not response_handle:headers():get("X-Frame-Options") then
                response_handle:headers():add("X-Frame-Options", "deny");
                end
                if not response_handle:headers():get("X-XSS-Protection") then
                response_handle:headers():add("X-XSS-Protection", "1; mode=block");
                end
                if not response_handle:headers():get("X-Content-Type-Options") then
                response_handle:headers():add("X-Content-Type-Options", "nosniff");
                end
                if not response_handle:headers():get("Referrer-Policy") then
                response_handle:headers():add("Referrer-Policy", "no-referrer");
                end
                if not response_handle:headers():get("X-Download-Options") then
                response_handle:headers():add("X-Download-Options", "noopen");
                end
                if not response_handle:headers():get("X-DNS-Prefetch-Control") then
                response_handle:headers():add("X-DNS-Prefetch-Control", "off");
                end
                if not response_handle:headers():get("Feature-Policy") then
                response_handle:headers():add("Feature-Policy",
                                                "camera 'none';"..
                                                "microphone 'none';"..
                                                "geolocation 'none';"..
                                                "encrypted-media 'none';"..
                                                "payment 'none';"..
                                                "speaker 'none';"..
                                                "usb 'none';");
                end
                if response_handle:headers():get("X-Powered-By") then
                response_handle:headers():remove("X-Powered-By");
                end
            end
EOF

How the Lua script works: For each response at the ingress gateway, the script checks whether each security header exists. If a header is absent, the script adds it with the default value. For Content-Security-Policy, it checks that a frame-ancestors directive is not already present before appending one. The script removes X-Powered-By to prevent server information disclosure.

Adjust proxyVersion to match your Istio version. The proxyVersion field determines which Envoy proxies the filter applies to:

  • Istio 1.9 or later: Set proxyVersion to your version, for example '^1\.17.*'. No other changes are required.

  • Istio 1.8 or earlier: Set proxyVersion to your version and make the following replacements:

    Original valueReplacement
    envoy.filters.network.http_connection_managerenvoy.http_connection_manager
    envoy.filters.http.routerenvoy.router
    type.googleapis.com/envoy.extensions.filters.http.lua.v3.Luatype.googleapis.com/envoy.config.filter.http.lua.v2.Lua

Verify the security headers

After deploying the EnvoyFilter, confirm that the security headers appear in the response.

curl -I http://<ingress-gateway-ip>/productpage

Expected output:

HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 4183
server: istio-envoy
date: Tue, 28 Jan 2020 09:07:01 GMT
x-envoy-upstream-service-time: 17
content-security-policy: frame-ancestors none;
x-frame-options: deny
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
referrer-policy: no-referrer
x-download-options: noopen
x-dns-prefetch-control: off
feature-policy: camera 'none';microphone 'none';geolocation 'none';encrypted-media 'none';payment 'none';speaker 'none';usb 'none';

All security headers from the reference table now appear in the response.

FAQ

Why can't I access my application when the URL contains special characters?

Envoy requires special characters in URLs to be percent-encoded per RFC 3986. If characters such as Á are Unicode-encoded rather than percent-encoded, Envoy cannot parse the URL. See Envoy cannot parse non-alphanumeric characters if not urlencoded for background.