Skip to content

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

CORS

Verified : Automated tests passed for: Kubernetes resources: The Kubernetes resources on this page were checked against their API schemas. End-to-end run: Every step ran in a fresh test environment and the results were checked.
Page as Markdown

Enforce client-site access controls with cross-origin resource sharing (CORS).

Enforce client-side access controls with cross-origin resource sharing (CORS).

About CORS

Cross-Origin Resource Sharing (CORS) is a security feature that is implemented by web browsers and that controls how web pages in one domain can request and interact with resources that are hosted on a different domain. By default, web browsers only allow requests to resources that are hosted on the same domain as the web page that served the original request. Access to web pages or resources that are hosted on a different domain is restricted to prevent potential security vulnerabilities, such as cross-site request forgery (CSRF).

When CORS is enabled in a web browser and a request for a different domain comes in, the web browser checks whether this request is allowed or not. To do that, it typically sends a preflight request (HTTP OPTIONS method) to the server or service that serves the requested resource. The service returns the methods that are permitted to send the actual cross-origin request, such as GET, POST, etc. If the request to the different domain is allowed, the response includes CORS-specific headers that instruct the web browser how to make the cross-origin request. For example, the CORS headers typically include the origin that is allowed to access the resource, and the credentials or headers that must be included in the cross-origin request.

Note that the preflight request is optional. Web browsers can also be configured to send the cross-origin directly. However, access to the request resource is granted only if CORS headers were returned in the response. If no headers are returned during the preflight request, the web browser denies access to the resource in the other domain.

CORS policies are typically implemented to limit access to server resources for JavaScripts that are embedded in a web page, such as:

  • A JavaScript on a web page at example.com tries to access a different domain, such as api.com.
  • A JavaScript on a web page at example.com tries to access a different subdomain, such as api.example.com.
  • A JavaScript on a web page at example.com tries to access a different port, such as example.com:3001.
  • A JavaScript on a web page at https://example.com tries to access the resources by using a different protocol, such as http://example.com.

Configuration options

You can configure the CORS policy at two levels:

  • HTTPRoute: For the native way in Kubernetes Gateway API, configure a CORS policy in the HTTPRoute. You can choose to apply the CORS policy to all the routes that are defined in the HTTPRoute, or to a selection of backendRefs. This route-level policy takes precedence over any TrafficPolicy CORS that you might configure. For more information, see the Kubernetes Gateway API docs and CORS design docs.
  • TrafficPolicy: For more flexibility to reuse the CORS policy across HTTPRoutes, specific routes and Gateways, configure a CORS policy in the TrafficPolicy. You can attach a TrafficPolicy to a Gateway, all HTTPRoutes via targetRefs, or an individual route via extensionRef. To attach to a backendRef, use a CORS policy in the HTTPRoute instead. For more information about attachment and merging rules, see the TrafficPolicy concept docs.

Before you begin

  1. Follow the Get started guide to install kgateway.

  2. Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.

  3. Get the external address of the gateway and save it in an environment variable.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS  

Set up CORS policies

Create a CORS policy for the httpbin app in an HTTPRoute or TrafficPolicy.

CORS in HTTPRoute

Create an HTTPRoute resource for the httpbin app that applies a CORS filter. The following example allows requests from the https://example.com/ origin.

kubectl apply -f- <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin-cors
  namespace: httpbin
spec:
  parentRefs:
    - name: http
      namespace: kgateway-system
  hostnames:
    - cors.example
  rules:
    - filters:
        - type: CORS
          cors:
            allowCredentials: true
            allowHeaders:
              - Origin               
            allowMethods:
              - GET
              - POST
              - OPTIONS               
            allowOrigins:
              - "https://example.com"
            exposeHeaders:
            - Origin
            - X-HTTPRoute-Header
            maxAge: 86400
      backendRefs:
        - name: httpbin
          port: 8000
EOF

CORS in TrafficPolicy

  1. Create a TrafficPolicy resource for the httpbin app that applies a CORS filter. The following example allows requests from the https://example.com/ origin.

    kubectl apply -f- <<EOF
    apiVersion: gateway.kgateway.dev/v1alpha1
    kind: TrafficPolicy
    metadata:
      name: httpbin-cors
      namespace: httpbin
    spec:
      cors:
        allowCredentials: true
        allowHeaders:
          - "Origin"
          - "Authorization"
          - "Content-Type"             
        allowMethods:
          - "GET"
          - "POST"
          - "OPTIONS"               
        allowOrigins:
          - "https://example.com"
        exposeHeaders:
        - "Origin"
        - "X-TrafficPolicy-Header"
        maxAge: 86400
    EOF
  2. Attach the TrafficPolicy to a route or Gateway. The following example creates an HTTPRoute for the httpbin app that has the TrafficPolicy attached via the extensionRef filter. For more information about attachment and merging rules, see the TrafficPolicy concept docs.

    kubectl apply -f- <<EOF
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: httpbin-cors
      namespace: httpbin
    spec:
      parentRefs:
        - name: http
          namespace: kgateway-system
      hostnames:
        - cors.example
      rules:
        - filters:
            - type: ExtensionRef
              extensionRef:
                group: gateway.kgateway.dev
                kind: TrafficPolicy
                name: httpbin-cors
          backendRefs:
            - name: httpbin
              port: 8000
    EOF

Test CORS policies

Now that you have CORS policies applied via an HTTPRoute or TrafficPolicy, you can test the policies.

  1. Send a request to the httpbin app on the cors.example domain and use https://example.com as the origin. Verify that your request succeeds and that you get back the configured CORS headers.

    curl -I -X OPTIONS http://$INGRESS_GW_ADDRESS:8080/get -H "host: cors.example:8080" \
     -H "Origin: https://example.com" \
     -H "Access-Control-Request-Method: POST" \
     -H "Access-Control-Request-Headers: Origin"

    Example output: Notice that the access-control-* values reflect your CORS policy and change depending on the resources that you created.

    • If you created an HTTPRoute with a CORS filter, you see the Origin and X-HTTPRoute-Header headers.
    • If you created a TrafficPolicy with a CORS filter, you see the Origin and X-TrafficPolicy-Header headers.

    CORS in HTTPRoute

    HTTP/1.1 200 OK
    x-correlation-id: aaaaaaaa
    date: Tue, 24 Jun 2025 13:19:53 GMT
    content-length: 0
    
    HTTP/1.1 200 OK
    access-control-allow-origin: https://example.com
    access-control-allow-credentials: true
    access-control-allow-methods: GET, POST, OPTIONS
    access-control-allow-headers: Origin
    access-control-max-age: 86400
    access-control-expose-headers: Origin, X-HTTPRoute-Header
    date: Tue, 24 Jun 2025 13:19:53 GMT
    server: envoy
    content-length: 0
    ...
    

    CORS in TrafficPolicy

    HTTP/1.1 200 OK
    x-correlation-id: aaaaaaaa
    date: Tue, 24 Jun 2025 13:19:53 GMT
    content-length: 0
    
    HTTP/1.1 200 OK
    access-control-allow-origin: https://example.com
    access-control-allow-credentials: true
    access-control-allow-methods: GET, POST, OPTIONS
    access-control-allow-headers: Origin, Authorization, Content-Type
    access-control-max-age: 86400
    access-control-expose-headers: Origin, X-TrafficPolicy-Header
    date: Tue, 24 Jun 2025 13:19:53 GMT
    server: envoy
    content-length: 0
    
  2. Send another request to the httpbin app. This time, you use notallowed.com as your origin. Because notallowed.com is not configured as a supported origin, the response does not include the access-control-allow-origin header for your origin.

    curl -I -X OPTIONS http://$INGRESS_GW_ADDRESS:8080/get -H "host: cors.example:8080" \
     -H "Origin: https://notallowed.com" \
     -H "Access-Control-Request-Method: POST" \
     -H "Access-Control-Request-Headers: Origin"

    The gateway answers the preflight request itself with a 200 response, and does not forward it to the httpbin app. The response has no access-control-allow-origin header, so the browser blocks the cross-origin request. The response still includes other values from your CORS policy, such as the allowed methods and the max age. These values have no effect without a matching access-control-allow-origin header. The access-control-allow-headers value depends on the resource that you created.

    Example output:

    HTTP/1.1 200 OK
    access-control-allow-credentials: true
    access-control-allow-methods: GET, POST, OPTIONS
    access-control-allow-headers: Origin
    access-control-max-age: 86400
    date: Thu, 05 Nov 2026 06:47:07 GMT
    server: envoy
    content-length: 0
    

Cleanup

You can remove the resources that you created in this guide.

CORS in HTTPRoute

kubectl delete httproute httpbin-cors -n httpbin

CORS in TrafficPolicy

kubectl delete httproute httpbin-cors -n httpbin
kubectl delete TrafficPolicy httpbin-cors -n httpbin
Was this page helpful?