Envoy’s CORS filter doesn’t actually handle CORS requests directly; it manipulates HTTP headers to tell the browser that CORS is allowed.
Let’s see this in action. Imagine a client browser trying to fetch a resource from your API, which is proxied by Envoy.
// Client Browser Request (simplified)
GET /data HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000
The Origin header is the key here. The browser sends it to indicate where the request is coming from. If your backend API doesn’t explicitly allow requests from http://localhost:3000, it might deny the request, and the browser will block it with a CORS error.
Here’s how Envoy steps in. You configure the Envoy CORS filter, typically in your http_filter chain.
# envoy.yaml (excerpt)
http_filters:
- name: envoy.filters.http.cors
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
# This is where the magic happens: specifying allowed origins
allow_origin_string: "http://localhost:3000"
# Optionally, specify allowed methods
allow_methods: GET,POST,PUT,DELETE,OPTIONS
# Optionally, specify allowed headers
allow_headers: "content-type,authorization"
# Optionally, specify max age for preflight requests
max_age: "86400" # 24 hours
When Envoy receives the client request with the Origin: http://localhost:3000 header, the CORS filter checks if http://localhost:3000 is in its allow_origin_string configuration. If it is, Envoy rewrites the request (or, more accurately, adds headers to the response that will be sent back to the browser) to include the necessary CORS headers.
The actual request that reaches your backend service remains largely unchanged. Envoy doesn’t need to know anything about CORS; it just acts as a gatekeeper based on the Origin header and its configuration.
The crucial part is what Envoy adds to the response that goes back to the browser:
// Envoy's Response to Browser (after filter processing)
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: content-type,authorization
Access-Control-Max-Age: 86400
// ... actual response body ...
The browser receives these headers and, seeing that Access-Control-Allow-Origin matches its own Origin, allows the JavaScript code to access the response. If the Origin header in the request didn’t match any configured allow_origin_string in Envoy, Envoy would simply pass the request through to the backend without adding these CORS headers. The backend might then reject it, or the browser would block it because it didn’t receive the necessary Access-Control-Allow-Origin header from the server (or proxy).
The CORS filter is designed to handle OPTIONS preflight requests automatically. When a browser makes an OPTIONS request to check if a subsequent cross-origin request is allowed, Envoy intercepts it. If the Origin, Access-Control-Request-Method, and Access-Control-Request-Headers in the preflight request match Envoy’s configuration, Envoy responds with a 200 OK and the appropriate Access-Control-Allow-* headers, allowing the browser to proceed with the actual request. If they don’t match, Envoy will typically return a 403 Forbidden or similar, preventing the browser from sending the actual request.
The most surprising thing about Envoy’s CORS filter is that it doesn’t perform any complex logic to validate the request against your backend’s specific CORS policies. It’s purely a header manipulation tool. If you configure it to allow http://localhost:3000, it will add Access-Control-Allow-Origin: http://localhost:3000 to all responses that originate from the configured upstream cluster, regardless of whether your backend service itself intended to allow that origin. This means the CORS filter is best used when your backend service has no built-in CORS handling, or when you want a centralized, proxy-level control over CORS for multiple services.
The next step is understanding how to handle more complex CORS scenarios, like dynamically allowing origins based on request parameters or integrating with external identity providers.