CORS Policy in NGINX Ingress Controller v5.4.0: Patterns for VirtualServer and Ingress

by

in , ,

Starting with NGINX Ingress Controller (NIC) v5.4.0, you can define CORS behavior once in a Policy resource and apply it consistently across both VirtualServer and Ingress traffic paths.

Across this blog, we’re focused on:

  • How CORS policy works in NGINX Ingress Controller.
  • Where to attach it in VirtualServer and Ingress.

Why Use a Policy For CORS?

Many teams start with per-resource tuning and quickly end up with drift. Using a dedicated Policy for CORS gives you:

  • A single source of truth for allowed origins, methods, and headers.
  • Reuse across services and namespaces.
  • Cleaner reviews because CORS behavior is isolated from route logic.

How CORS Policy Works in NGINX Ingress Controller

At a high level:

  1. Create a Policy resource with spec.cors
  2. Attach it where traffic is defined:
    • VirtualServer.spec.policies (or route/subroute policies)
    • Ingress via the nginx.org/policies annotation
  3. NGINX Ingress Controller renders the corresponding NGINX CORS behavior and returns headers for preflight and actual cross-origin requests.

Example CORS Policy:

apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
   name: cors-policy
spec:
   cors:
      allowOrigin:
      - https://app.example.com
      allowMethods:
      - GET
      - POST
      - PUT
      - OPTIONS
      allowHeaders:
      - Content-Type
      - Authorization
      - X-Requested-With
      exposeHeaders:
      - X-Total-Count
      - X-Page-Size
      allowCredentials: true
      maxAge: 86400

Example of VirtualServer referencing above CORS Policy:

apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
   name: webapp
spec:
   host: webapp.example.com
   policies:
   - name: cors-policy
   upstreams:
   - name: webapp
      service: webapp-svc
      port: 80
   routes:
   - path: /test
      action:
         pass: webapp

Example of Ingress referencing above CORS Policy:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
   name: cafe-ingress
   annotations:
      nginx.org/policies: "cors-policy"
spec:
   ingressClassName: nginx
   rules:
   - host: cafe.example.com
      http:
         paths:
         - path: /tea
            pathType: Prefix
            backend:
               service:
                  name: tea-svc
                  port:
                     number: 80

Important behavior to remember:

  • CORS policy can be applied to both VirtualServer and Ingress.
  • For VirtualServer, route/subroute policies override same-type policy at spec level.
  • For credentialed CORS (allowCredentials: true), explicit origins are required.

Production Checks That Prevent Most Issues

Before rollout, validate these explicitly:

  • Non-allowed origins do not receive Access-Control-Allow-Origin.
  • allowCredentials: true is only used with explicit origins (not *).

Security Considerations

CORS is a browser enforcement boundary, so keep it tight:

  • Use explicit origin allow-lists for production.
  • Avoid broad method/header lists unless required.
  • Treat allowCredentials as high trust; use narrowly scoped origins.
  • Review CORS policy changes with the same care as auth-related config changes.

You can find complete working examples on github and more documentation in our docs:

NGINX Community Forum