All Products
Search
Document Center

Container Service for Kubernetes:CORS configuration for Nginx Ingress

Last Updated:Mar 27, 2026

When a browser requests a service in an ACK cluster, a blocked by CORS policy error occurs because browsers enforce the same-origin policy, which restricts a web page to resources from the same protocol, domain, and port. For example, https://example.com cannot directly access https://api.example.com. Configure annotations on your Nginx Ingress resource to enable Cross-Origin Resource Sharing (CORS) and allow specific cross-origin requests.

How it works

CORS handles two types of requests: simple requests and preflight requests.

A simple request is sent directly to the server:

  1. The browser adds the Origin header to the request—for example, Origin: https://example.com.

  2. The Nginx Ingress controller compares the HTTP method and Origin value against the CORS configuration. If they match, it adds Access-Control-Allow-Origin to the response.

  3. The browser checks whether Access-Control-Allow-Origin matches the request's origin. A match means success; a missing or mismatched header means failure.

A preflight request sends an OPTIONS check before the main request:

  1. The browser sends an OPTIONS request with Access-Control-Request-Method and Access-Control-Request-Headers describing the actual request.

  2. If the method or headers are not permitted, the preflight fails and the main request is never sent. If the preflight succeeds, the main request proceeds like a simple request.

A request triggers a preflight when any of the following conditions are met:

  • The method is not GET, HEAD, or POST.

  • The method is POST and Content-Type is not text/plain, application/x-www-form-urlencoded, or multipart/form-data.

  • The request includes a custom header.

Important

When cors-allow-credentials is "true", do not set cors-allow-origin to "*". The W3C specification prohibits this combination: when a request carries credentials (such as cookies), the server must explicitly name the trusted origin. Using "*" allows any website to send credentialed requests on a user's behalf, which is a security risk.

Configure CORS

Prerequisites

Before you begin, ensure that you have:

  • An ACK cluster with Nginx Ingress controller deployed

  • An Ingress resource that routes traffic to your backend service

  • The permissions to edit Ingress resources in the cluster

Add CORS annotations

  1. Log on to the ACK console. In the left-side navigation pane, choose Clusters.

  2. On the Clusters page, click the name of the target cluster. In the left-side navigation pane, choose Network > Ingresses.

  3. On the Ingresses page, find the target Ingress and click Edit YAML in the Actions column.

  4. Add CORS annotations to the metadata.annotations section based on your scenario.

Scenario A: Requests with credentials or cookies (recommended)

Use this configuration when a frontend application (https://example.com or https://app.example.com) needs to include credentials—such as cookies or an Authorization header—to access a backend API (https://api.example.com).

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress-secure
  annotations:
    # Enable CORS.
    nginx.ingress.kubernetes.io/enable-cors: "true"
    # Allow requests with credentials, such as cookies and Authorization headers.
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    # Specify exact origins. Do not use "*" when credentials are enabled.
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com, https://app.example.com"
    # Specify the allowed HTTP methods.
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
    # Specify the allowed request headers, including any custom headers your application requires.
    nginx.ingress.kubernetes.io/cors-allow-headers: "Content-Type, Authorization"
    # Specify which custom response headers are exposed to browser JavaScript.
    nginx.ingress.kubernetes.io/cors-expose-headers: "X-Request-ID, Content-Length, Content-Range"
    # Set the preflight cache duration in seconds. 86400 = 24 hours.
    nginx.ingress.kubernetes.io/cors-max-age: "86400"
...

Scenario B: Requests without credentials

Use this configuration for public, read-only APIs that do not require authentication.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress-public
  annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "*"
    # Must be "false" when cors-allow-origin is "*".
    nginx.ingress.kubernetes.io/cors-allow-credentials: "false"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, HEAD"
...

Verify CORS configuration

Use curl to simulate a browser preflight request:

curl -i -X OPTIONS 'https://api.example.com/your/path' \
  -H 'Origin: https://app.example.com' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: Content-Type, Authorization'

A successful preflight returns a 2xx status code, typically 204 No Content or 200 OK:

HTTP/2 204
date: Fri, 12 Sep 2025 03:51:12 GMT
access-control-allow-origin: https://example.com, https://app.example.com
access-control-allow-credentials: true
access-control-allow-methods: GET, POST, PUT, DELETE, OPTIONS
access-control-allow-headers: Content-Type, Authorization

Confirm that each access-control-allow-* value matches the corresponding nginx.ingress.kubernetes.io/cors-* annotation in your Ingress resource.

CORS annotations reference

<table> <thead> <tr> <td><b>Annotation</b></td> <td><b>Description</b></td> <td><b>HTTP header</b></td> <td><b>Example</b></td> </tr> </thead> <tbody> <tr> <td><code>nginx.ingress.kubernetes.io/enable-cors</code></td> <td>Enables or disables CORS.</td> <td>N/A</td> <td><code>nginx.ingress.kubernetes.io/enable-cors: "true"</code></td> </tr> <tr> <td><code>nginx.ingress.kubernetes.io/cors-allow-origin</code></td> <td>The origins allowed to access the resource. Separate multiple origins with a comma.</td> <td><code>Access-Control-Allow-Origin</code></td> <td><code>nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"</code></td> </tr> <tr> <td><code>nginx.ingress.kubernetes.io/cors-allow-methods</code></td> <td>The allowed HTTP methods.</td> <td><code>Access-Control-Allow-Methods</code></td> <td><code>nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS"</code></td> </tr> <tr> <td><code>nginx.ingress.kubernetes.io/cors-allow-headers</code></td> <td>The allowed custom request headers.</td> <td><code>Access-Control-Allow-Headers</code></td> <td><code>nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range"</code></td> </tr> <tr> <td><code>nginx.ingress.kubernetes.io/cors-allow-credentials</code></td> <td>Specifies whether to allow requests with credentials, such as cookies or HTTP authentication. Must be <code>"false"</code> when <code>cors-allow-origin</code> is <code>"*"</code>.</td> <td><code>Access-Control-Allow-Credentials</code></td> <td><code>nginx.ingress.kubernetes.io/cors-allow-credentials: "true"</code></td> </tr> <tr> <td><code>nginx.ingress.kubernetes.io/cors-expose-headers</code></td> <td>The response headers exposed to browser JavaScript. By default, only standard headers (<code>Cache-Control</code>, <code>Content-Language</code>, <code>Content-Type</code>, <code>Expires</code>, <code>Last-Modified</code>, <code>Pragma</code>) are accessible. Use this annotation to expose custom headers such as <code>X-Request-ID</code>.<br><br><b>Requires Nginx Ingress controller v0.44 or later.</b></td> <td><code>Access-Control-Expose-Headers</code></td> <td><code>nginx.ingress.kubernetes.io/cors-expose-headers: "Content-Length,Content-Range"</code></td> </tr> <tr> <td><code>nginx.ingress.kubernetes.io/cors-max-age</code></td> <td>The maximum time, in seconds, that a browser can cache the preflight response. A longer value reduces the number of preflight requests. For stricter security, use a lower value.</td> <td><code>Access-Control-Max-Age</code></td> <td><code>nginx.ingress.kubernetes.io/cors-max-age: "86400"</code></td> </tr> </tbody> </table>

FAQ

How do I troubleshoot cross-origin errors?

Check the network requests in your browser's developer tools, or inspect the Nginx Ingress controller logs. Verify that the request's origin, method, and headers match what is permitted in your Ingress CORS annotations.

Common error messages and their causes:

  • Method POST is not allowed by Access-Control-Allow-Methods in preflight response — the method used is not listed in cors-allow-methods.

  • Access to fetch at 'https://api.example.com/data' from origin 'https://app.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: xxxx. — the preflight check failed; compare the access-control-allow-* response headers against your annotation values.

How do I configure different CORS policies for different paths on the same domain?

CORS annotations apply at the Ingress resource level; path-level configuration is not supported. Create separate Ingress resources for groups of paths that need different policies. For example, create api-public-ingress.yaml for public paths and api-private-ingress.yaml for authenticated paths, and set different CORS annotations on each.

How do I expose custom response headers to browser JavaScript?

By default, browsers can only access the following standard response headers in cross-origin responses: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma. Custom headers returned by the server—such as X-Request-ID—are hidden from client-side JavaScript unless explicitly exposed.

Add cors-expose-headers to your annotations to expose them. For a full example, see Scenario A: Requests with credentials or cookies.

References