Browsers enforce the same-origin policy, restricting web pages to resources from the same protocol, domain, and port. A request from https://example.com to https://api.example.com triggers a blocked by CORS policy error. Add Cross-Origin Resource Sharing (CORS) annotations to your Nginx Ingress to allow specific cross-origin requests.
How it works
CORS handles two request types: simple and preflight.
A simple request is sent directly to the server:
-
The browser adds the
Originheader to the request—for example,Origin: https://example.com. -
The Nginx Ingress controller compares the HTTP method and
Originvalue against the CORS configuration. If they match, it addsAccess-Control-Allow-Originto the response. -
The browser checks whether
Access-Control-Allow-Originmatches 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:
-
The browser sends an
OPTIONSrequest withAccess-Control-Request-MethodandAccess-Control-Request-Headersdescribing the actual request. -
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:
-
The method is not
GET,HEAD, orPOST. -
The method is
POSTandContent-Typeis nottext/plain,application/x-www-form-urlencoded, ormultipart/form-data. -
The request includes a custom header.
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
-
Permissions to edit Ingress resources in the cluster
Add CORS annotations
-
Log on to the ACK console. In the left-side navigation pane, choose Clusters.
-
On the Clusters page, click the target cluster. In the left-side navigation pane, choose Network > Ingresses.
-
On the Ingresses page, find the target Ingress and click Edit YAML in the Actions column.
-
Add CORS annotations to
metadata.annotationsbased on your scenario.
Scenario A: Requests with credentials or cookies (recommended)
Use this when your frontend (https://example.com or https://app.example.com) sends credentials—such as cookies or an Authorization header—to 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 for public, read-only APIs without 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.
CORS annotations reference
|
Annotation |
Description |
HTTP header |
Example |
|
|
Enables or disables CORS. |
N/A |
|
|
|
Allowed origins. Separate multiple origins with commas. |
|
|
|
|
The allowed HTTP methods. |
|
|
|
|
The allowed custom request headers. |
|
|
|
|
Specifies whether to allow requests with credentials, such as cookies or HTTP authentication. Must be |
|
|
|
|
Response headers exposed to browser JavaScript. By default, only standard headers ( Requires Nginx Ingress controller v0.44 or later. |
|
|
|
|
Maximum time (seconds) a browser can cache the preflight response. Longer values reduce preflight requests; shorter values improve security. |
|
|
FAQ
How do I troubleshoot cross-origin errors?
Check network requests in your browser's developer tools or the Nginx Ingress controller logs. Verify that the request's origin, method, and headers match 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 is not listed incors-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.— preflight failed; compareaccess-control-allow-*response headers with your annotation values.
How do I configure different CORS policies for different paths on the same domain?
CORS annotations apply at the Ingress level; path-level configuration is not supported. Create separate Ingress resources for path groups that need different policies—for example, api-public-ingress.yaml for public paths and api-private-ingress.yaml for authenticated paths.
How do I expose custom response headers to browser JavaScript?
In cross-origin responses, browsers can only access these standard headers: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma. Custom headers—such as X-Request-ID—are hidden from JavaScript unless explicitly exposed.
Add cors-expose-headers to your annotations to expose them. See Scenario A: Requests with credentials or cookies.
References
-
Enable CORS — official ingress-nginx annotation reference