Cloud Native 16 min read

Mastering CORS Filters in Istio: Configure Envoy for Secure Cross‑Origin Requests

This guide explains what an Envoy CORS filter is, details its configuration options, and provides step‑by‑step VirtualService and EnvoyFilter examples for simple, credentialed, prefix, regex, exposed‑header, non‑simple requests and shadow logging in Istio.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Mastering CORS Filters in Istio: Configure Envoy for Secure Cross‑Origin Requests

What is a CORS filter

CORS (Cross‑origin resource sharing) enables resources to be shared across different domains. In Envoy, the CORS filter is an HTTP filter named envoy.filters.http.cors with type URL envoy.extensions.filters.http.cors.v3.Cors . Implementation details are omitted.

Configuration Options

allow_origin_string_match:   // allowed client origins
allow_methods: "GET,OPTIONS" // allowed HTTP methods
allow_headers: "content-type" // allowed request headers
allow_credentials: true      // whether cookies are allowed
exposeHeaders:               // response headers exposed to the caller
- test
- test2
max_age: "60"                // cache duration for preflight responses
filter_enabled:              // activation flag
  default_value:
    numerator: 0
    denominator: HUNDRED
shadow_enabled:               // logging flag
  default_value:
    numerator: 100
    denominator: HUNDRED

Practical Examples

3.1 Simple Request

VirtualService implementation

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
spec:
  exportTo:
  - '*'
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    corsPolicy:
      allowOrigins:
      - exact: "http://192.168.229.134:80"
    route:
    - destination:
        host: productpage
        port:
          number: 9080

EnvoyFilter implementation

cat << EOF > ef-cors-allow_origin_string_match.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: cors
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        name: 0.0.0.0_8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_route
              domains:
              - "*"
              cors:
                allow_origin_string_match:
                - exact: "http://192.168.229.134"
                filter_enabled:
                  default_value:
                    numerator: 100
                    denominator: HUNDRED
                routes:
                - match:
                    path: "/productpage"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/static"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/login"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/logout"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/api/v1/products"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
EOF

kubectl apply -f ef-cors-allow_origin_string_match.yaml -n istio-system --context context-cluster1

3.2 Simple Request with allowCredentials

VirtualService implementation

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
spec:
  exportTo:
  - '*'
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    corsPolicy:
      allowCredentials: true
      allowOrigins:
      - exact: "http://192.168.229.134"
    route:
    - destination:
        host: productpage
        port:
          number: 9080

EnvoyFilter implementation

cat << EOF > ef-cors-allowCredentials.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: cors
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        name: 0.0.0.0_8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_route
              domains:
              - "*"
              cors:
                allow_origin_string_match:
                - exact: "http://192.168.229.134"
                allow_credentials: true
                filter_enabled:
                  default_value:
                    numerator: 100
                    denominator: HUNDRED
                routes:
                - match:
                    path: "/productpage"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/static"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/login"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/logout"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/api/v1/products"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
EOF

kubectl apply -f ef-cors-allowCredentials.yaml -n istio-system --context context-cluster1

3.3 Simple Request with allowOrigins prefix

VirtualService implementation

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
spec:
  exportTo:
  - '*'
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    corsPolicy:
      allowOrigins:
      - prefix: "http://192"
    route:
    - destination:
        host: productpage
        port:
          number: 9080

EnvoyFilter implementation

cat << EOF > ef-cors-allow_origin_string_match-prefix.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: cors
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        name: 0.0.0.0_8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_route
              domains:
              - "*"
              cors:
                allow_origin_string_match:
                - prefix: "http://192"
                allow_credentials: true
                filter_enabled:
                  default_value:
                    numerator: 100
                    denominator: HUNDRED
                routes:
                - match:
                    path: "/productpage"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/static"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/login"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/logout"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/api/v1/products"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
EOF

kubectl apply -f ef-cors-allow_origin_string_match-prefix.yaml -n istio-system --context context-cluster1

3.4 Simple Request with allowOrigins regex

VirtualService implementation

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
spec:
  exportTo:
  - '*'
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    corsPolicy:
      allowOrigins:
      - regex: ".*"
    route:
    - destination:
        host: productpage
        port:
          number: 9080

EnvoyFilter implementation

cat << EOF > ef-cors-allow_origin_string_match-regex.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: cors
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        name: 0.0.0.0_8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_route
              domains:
              - "*"
              cors:
                allow_origin_string_match:
                - safeRegex:
                    googleRe2: {}
                    regex: .*
                allow_credentials: true
                filter_enabled:
                  default_value:
                    numerator: 100
                    denominator: HUNDRED
                routes:
                - match:
                    path: "/productpage"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/static"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/login"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/logout"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/api/v1/products"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
EOF

kubectl apply -f ef-cors-allow_origin_string_match-regex.yaml -n istio-system --context context-cluster1

3.5 Simple Request with exposeHeaders

VirtualService implementation

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
spec:
  exportTo:
  - '*'
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    corsPolicy:
      allowOrigins:
      - exact: "http://192.168.229.134"
      exposeHeaders:
      - test
      - test2
    route:
    - destination:
        host: productpage
        port:
          number: 9080

EnvoyFilter implementation

cat << EOF > ef-cors-exposeHeaders.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: cors
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        name: 0.0.0.0_8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_route
              domains:
              - "*"
              cors:
                allow_origin_string_match:
                - safeRegex:
                    googleRe2: {}
                    regex: .*
                exposeHeaders: test,test2
                filter_enabled:
                  default_value:
                    numerator: 100
                    denominator: HUNDRED
                routes:
                - match:
                    path: "/productpage"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/static"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/login"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    path: "/logout"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
                - match:
                    prefix: "/api/v1/products"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
EOF

kubectl apply -f ef-cors-exposeHeaders.yaml -n istio-system --context context-cluster1

3.6 Non‑Simple Request

VirtualService implementation

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookreviews
spec:
  exportTo:
  - '*'
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        prefix: /reviews
    corsPolicy:
      allowOrigins:
      - exact: "http://192.168.229.134"
      allowMethods:
      - GET
      - OPTIONS
      maxAge: "1m"
      allowHeaders:
      - content-type
    route:
    - destination:
        host: reviews
        port:
          number: 9080

EnvoyFilter implementation

cat << EOF > ef-cors-not-simple.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: cors
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        name: 0.0.0.0_8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_route
              domains:
              - "*"
              cors:
                allow_origin_string_match:
                - exact: "http://192.168.229.134"
                allow_methods: "GET,OPTIONS"
                allow_headers: "content-type"
                max_age: "60"
                filter_enabled:
                  default_value:
                    numerator: 100
                    denominator: HUNDRED
                routes:
                - match:
                    prefix: "/reviews"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
EOF

kubectl apply -f ef-cors-not-simple.yaml -n istio-system --context context-cluster1

3.7 shadow_enabled

VirtualService cannot implement shadow_enabled; use EnvoyFilter

cat << EOF > ef-cors-shadow.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: cors
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        name: 0.0.0.0_8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_route
              domains:
              - "*"
              cors:
                allow_origin_string_match:
                - exact: "http://192.168.229.134"
                allow_methods: "GET,OPTIONS"
                allow_headers: "content-type"
                max_age: "60"
                filter_enabled:
                  default_value:
                    numerator: 0
                    denominator: HUNDRED
                shadow_enabled:
                  default_value:
                    numerator: 100
                    denominator: HUNDRED
                routes:
                - match:
                    prefix: "/reviews"
                  route:
                    cluster: outbound|9080||productpage.istio.svc.cluster.local
EOF

kubectl apply -f ef-cors-shadow.yaml -n istio-system --context context-cluster1
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Cloud NativeIstioCORSEnvoyEnvoyFilterVirtualService
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.