# Policy resources




The Policy resource lets you define reusable configuration such as access control, CORS, egress mTLS, and WAF, and then attach that configuration to [VirtualServer and VirtualServerRoute resources](/nic/configuration/virtualserver-and-virtualserverroute-resources.md) or [Ingress resources](/nic/configuration/ingress-resources).

The resource is implemented as a [Custom Resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/).

## Before you begin

Policies work together with [VirtualServer, VirtualServerRoute resources](/nic/configuration/virtualserver-and-virtualserverroute-resources.md) and [Ingress resources](/nic/configuration/ingress-resources), which you create separately.

## Policy Specification

A `Policy` resource defines exactly one policy type under `.spec`.

Supported policy types are:

- `accessControl`
- `rateLimit`
- `apiKey`
- `basicAuth`
- `jwt`
- `ingressMTLS`
- `egressMTLS`
- `oidc`
- `cache`
- `cors`
- `waf`
- `externalAuth`

| Policy type | Description | VirtualServer / VirtualServerRoute | Ingress |
| --- | --- | --- | --- |
| [`accessControl`](#accesscontrol) | The access control policy based on the client IP address. | Yes | Yes, with `nginx.org/policies` |
| [`cors`](#cors) | The CORS policy configures Cross-Origin Resource Sharing headers. | Yes | Yes, with `nginx.org/policies` |
| [`egressMTLS`](#egressmtls) | The EgressMTLS policy configures upstreams authentication and certificate verification. | Yes | Yes, with `nginx.org/policies` |
| [`ingressMTLS`](#ingressmtls) | The IngressMTLS policy configures client certificate verification. | Yes | Yes, with `nginx.org/policies` |
| [`waf`](#waf) | The WAF policy configures WAF and log configuration policies for [NGINX AppProtect](/nic/integrations/app-protect-waf/configuration.md). | Yes | Yes, with `nginx.com/policies` |
| [`externalAuth`](#externalauth) | The External Auth policy configures NGINX to authenticate client requests using an external authentication server. | Yes | Yes, with `nginx.org/policies` |
| [`rateLimit`](#ratelimit) | The rate limit policy controls the rate of processing requests per a defined key. | Yes | No |
| [`apiKey`](#apikey) | The API Key policy configures NGINX to authorize requests which provide a valid API Key in a specified header or query param. | Yes | No |
| [`basicAuth`](#basicauth) | The basic auth policy configures NGINX to authenticate client requests using HTTP Basic authentication credentials. | Yes | No |
| [`jwt`](#jwt-using-local-kubernetes-secret) | The JWT policy configures NGINX Plus to authenticate client requests using JSON Web Tokens. | Yes | No |
| [`oidc`](#oidc) | The OIDC policy configures NGINX Plus as a relying party for OpenID Connect authentication. | Yes | No |
| [`cache`](#cache) | The cache policy configures proxy caching for serving cached content. | Yes | No |

**note:** 

Policy resource support for Ingress objects using annotation [`nginx.org/policies`](/nic/configuration/ingress-resources/advanced-configuration-with-annotations.md) was introduced in NGINX Ingress Controller v5.4.0.

## Important rule: One Policy type per resource

A `Policy` resource must define exactly one policy type under `.spec`.

The following example is valid:

```yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: allow-localhost
spec:
  accessControl:
    allow:
    - 10.0.0.0/8
```

The following example is **not** valid:

```yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: invalid-policy
spec:
  accessControl:
    allow:
    - 10.0.0.0/8
  cors:
    allowOrigin:
    - https://example.com
```

If you need multiple behaviors, create multiple policies and reference them together.

## Applying policies

Policies can be referenced from the following resources:

- `VirtualServer`
- `VirtualServerRoute`
- `Ingress`

How you attach them depends on the resource type.

### VirtualServer

You can attach policies at:

- `spec.policies` for server-wide behavior
- `spec.routes[].policies` for route-specific behavior

Example:

```yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: cafe
spec:
  host: cafe.example.com
  policies:
  - name: access-policy
  upstreams:
  - name: coffee
    service: coffee-svc
    port: 80
  routes:
  - path: /coffee
    policies:
    - name: route-cors-policy
    action:
      pass: coffee
```

### VirtualServerRoute

You can attach policies at:

- `spec.subroutes[].policies`

Example:

```yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServerRoute
metadata:
  name: tea
spec:
  host: cafe.example.com
  upstreams:
  - name: tea
    service: tea-svc
    port: 80
  subroutes:
  - path: /tea
    policies:
    - name: subroute-policy
    action:
      pass: tea
```

### Ingress

Ingress uses annotations rather than a `policies` field.

Supported annotations are:

- `nginx.org/policies`
- `nginx.com/policies`

Example:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp
  annotations:
    nginx.org/policies: access-policy,cors-policy
spec:
  ingressClassName: nginx
  rules:
  - host: webapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp
            port:
              number: 80
```

## Ingress-specific behavior

### Ingress does not support every Policy type

Ingress policy support is narrower than `VirtualServer` support.

If you need route-level control for features like JWT, OIDC, cache, or rate limiting, use `VirtualServer` and `VirtualServerRoute` instead.

### WAF on Ingress must use `nginx.com/policies`

WAF is a Plus-only feature. When using a WAF policy with Ingress, reference it through:

```yaml
metadata:
  annotations:
    nginx.com/policies: waf-policy
```

Do not attach WAF to Ingress with `nginx.org/policies`.

**important:** 

On Ingress, `nginx.org/policies` and `nginx.com/policies` are not interchangeable. WAF policies must be referenced through `nginx.com/policies`.

### Egress mTLS on Ingress only configures TLS parameters

`egressMTLS` defines how NGINX should authenticate to the upstream and verify the upstream certificate.

It does not switch the upstream transport from plain HTTP to HTTPS.

For Ingress, you still need the upstream connection itself to use TLS.

Typical ways that happens are:

- `nginx.org/ssl-services` for HTTPS upstreams
- `nginx.org/grpc-services` for gRPC upstreams

For example:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
  annotations:
    nginx.org/policies: egress-mtls-policy
    nginx.org/ssl-services: "secure-app"
spec:
  ingressClassName: nginx
  rules:
  - host: webapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: secure-app
            port:
              number: 8443
```

Without upstream TLS enabled, the egress mTLS policy has TLS settings to apply, but no TLS connection to apply them to.

In practice, that usually results in NGINX sending plain HTTP to an HTTPS upstream port.

**Note:** 

For Ingress, `egressMTLS` configures how NGINX uses TLS when connecting to the upstream. It does not decide whether the upstream connection uses TLS. That is controlled separately, for example with `nginx.org/ssl-services` or `nginx.org/grpc-services`.

### Mergeable Ingress behavior

For mergeable Ingress:

- policies on the master apply to inherited minion configuration
- policies on the minion override policies of the same type from the master

This matches the general expectation that a more specific resource overrides a broader one.

For `egressMTLS`, there is one extra detail:

- master or standard Ingress policy is applied at server scope
- minion override is applied at location scope so the minion can replace the master's value

## Precedence and override rules

### VirtualServer and VirtualServerRoute

Policy precedence goes from broader scope to narrower scope:

- `VirtualServer.spec.policies`
- `VirtualServer.route.policies`
- `VirtualServerRoute.subroute.policies`

If the same policy type appears at multiple levels, the more specific level wins.

Examples:

- route-level `accessControl` overrides spec-level `accessControl`
- subroute-level `cors` overrides route-level `cors`

### Ingress and Mergeable Ingress

For Ingress:

- policies apply to the whole Ingress
- with mergeable Ingress, minion policies override master policies of the same type

## Detailed policy reference

### AccessControl

The access control policy configures NGINX to deny or allow requests from clients with the specified IP addresses/subnets.

For example, the following policy allows access for clients from the subnet `10.0.0.0/8` and denies access for any other clients:

```yaml
accessControl:
  allow:
  - 10.0.0.0/8
```

In contrast, the policy below does the opposite: denies access for clients from `10.0.0.0/8` and allows access for any other clients:

```yaml
accessControl:
  deny:
  - 10.0.0.0/8
```

**Note:** 

The feature is implemented using the NGINX [ngx_http_access_module](http://nginx.org/en/docs/http/ngx_http_access_module.html). NGINX Ingress Controller access control policy supports either allow or deny rules, but not both (as the module does).

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``allow`` | Allows access for the specified networks or addresses. For example, ``192.168.1.1`` or ``10.1.1.0/16``. | ``[]string`` | No |
|``deny`` | Denies access for the specified networks or addresses. For example, ``192.168.1.1`` or ``10.1.1.0/16``. | ``[]string`` | No | \* an accessControl must include either `allow` or `deny`. |

#### AccessControl Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple access control policies. For example, here we reference two policies, each with configured allow lists:

```yaml
policies:
- name: allow-policy-one
- name: allow-policy-two
```

When you reference more than one access control policy, NGINX Ingress Controller will merge the contents into a single allow list or a single deny list.

Referencing both allow and deny policies, as shown in the example below, is not supported. If both allow and deny lists are referenced, NGINX Ingress Controller uses just the allow list policies.

```yaml
policies:
- name: deny-policy
- name: allow-policy-one
- name: allow-policy-two
```

### RateLimit

The rate limit policy configures NGINX to limit the processing rate of requests.

For example, the following policy will limit all subsequent requests coming from a single IP address once a rate of 10 requests per second is exceeded:

```yaml
rateLimit:
  rate: 10r/s
  zoneSize: 10M
  key: ${binary_remote_addr}
```

**Note:** 

The feature is implemented using the NGINX [ngx_http_limit_req_module](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html).

**Note:** 

When the [Zone Sync feature](/nic/configuration/global-configuration/configmap-resource.md#zone-sync) is enabled with NGINX Plus, the rate limiting zone will be synchronized across all replicas in the cluster.  This means all replicas are aware of the requests that have been rate limited by other replicas in the cluster.

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``rate`` | The rate of requests permitted. The rate is specified in requests per second (r/s) or requests per minute (r/m). | ``string`` | Yes |
|``key`` | The key to which the rate limit is applied. Can contain text, variables, or a combination of them. Variables must be surrounded by ``${}``. For example: ``${binary_remote_addr}``. Accepted variables are ``$binary_remote_addr``, ``$request_uri``,``$request_method``, ``$url``, ``$http_``, ``$args``, ``$arg_``, ``$cookie_``, ``$jwt_claim_``. | ``string`` | Yes |
|``zoneSize`` | Size of the shared memory zone. Only positive values are allowed. Allowed suffixes are ``k`` or ``m``, if none are present ``k`` is assumed. | ``string`` | Yes |
|``delay`` | The delay parameter specifies a limit at which excessive requests become delayed. If not set all excessive requests are delayed. | ``int`` | No |
|``noDelay`` | Disables the delaying of excessive requests while requests are being limited. Overrides ``delay`` if both are set. | ``bool`` | No |
|``burst`` | Excessive requests are delayed until their number exceeds the ``burst`` size, in which case the request is terminated with an error. | ``int`` | No |
|``dryRun`` | Enables the dry run mode. In this mode, the rate limit is not actually applied, but the number of excessive requests is accounted as usual in the shared memory zone. | ``bool`` | No |
|``logLevel`` | Sets the desired logging level for cases when the server refuses to process requests due to rate exceeding, or delays request processing. Allowed values are ``info``, ``notice``, ``warn`` or ``error``. Default is ``error``. | ``string`` | No |
|``rejectCode`` | Sets the status code to return in response to rejected requests. Must fall into the range ``400..599``. Default is ``503``. | ``int`` | No |
|``scale`` | Enables a constant rate-limit by dividing the configured rate by the number of nginx-ingress pods currently serving traffic. This adjustment ensures that the rate-limit remains consistent, even as the number of nginx-pods fluctuates due to autoscaling. **This will not work properly if requests from a client are not evenly distributed across all ingress pods** (Such as with sticky sessions, long lived TCP Connections with many requests, and so forth). In such cases using [zone-sync](/nic/configuration/global-configuration/configmap-resource.md#zone-sync) instead would give better results.  Enabling `zone-sync` will suppress this setting. | ``bool`` | No |
|``condition`` | Add a condition to a rate-limit policy. | [ratelimit.condition](#ratelimitcondition) | No |

**Note:** 

For each policy referenced in a VirtualServer and/or its VirtualServerRoutes, NGINX Ingress Controller will generate a single rate limiting zone defined by the [`limit_req_zone`](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone) directive. If two VirtualServer resources reference the same policy, NGINX Ingress Controller will generate two different rate limiting zones, one zone per VirtualServer.

#### RateLimit Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple rate limit policies. For example, here we reference two policies:

```yaml
policies:
- name: rate-limit-policy-one
- name: rate-limit-policy-two
```

When you reference more than one rate limit policy, NGINX Ingress Controller will configure NGINX to use all referenced rate limits. When you define multiple policies, each additional policy inherits the `dryRun`, `logLevel`, and `rejectCode` parameters from the first policy referenced (`rate-limit-policy-one`, in the example above).

### RateLimit.Condition

RateLimit.Condition defines a condition for a rate limit policy. For example:

```yaml
condition:
  jwt:
    claim: user_details.level
    match: premium
  default: true
```

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``jwt`` | defines a JWT condition to rate limit against. | [ratelimit.condition.jwt](#ratelimitconditionjwt) | No |
|``variables`` | defines a Variable condition to rate limit against. | [ratelimit.condition.variables](#ratelimitconditionvariables) | No |
|``default`` | sets the rate limit in this policy to be the default if no conditions are met. In a group of policies with the same condition, only one policy can be the default. | ``bool`` | No |

**Note:** 
Conditions (`jwt` or `variables`) are optional, but each policy can only have one. 
If conditions are used, a request doesn't match any, and a `default` has been defined, the `default` policy applies. Otherwise, if no `default` is set, the request isn't rate limited.

The rate limit policy with condition is designed to be used in combination with one or more rate limit policies. For example, multiple rate limit policies with [RateLimit.Condition.JWT](#ratelimitconditionjwt) can be used to apply different tiers of rate limit based on the value of a JWT claim. For a practical example of tiered rate limiting by the value of a JWT claim, see the example in our [GitHub repository](https://github.com/nginx/kubernetes-ingress/tree/v/examples/custom-resources/rate-limit-tiered-jwt-claim/README.md).

### RateLimit.Condition.JWT

**Note:** 

This feature is only available with NGINX Plus.

RateLimit.Condition.JWT defines a condition for a rate limit by JWT claim. For example, here we define a condition for a rate limit policy that only applies to requests with a JWT claim `user_details.level` with a value `premium`:

```yaml
jwt:
  claim: user_details.level
  match: premium
```

The rate limit policy will only apply to requests that contain a JWT with the specified claim and value. For example, the following JWT payload will match the JWT condition:

```json
{
  "user_details": {
    "level": "premium"
  },
  "sub": "client1"
}
```

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``claim`` | Claim is the JWT claim to be rate limit by. Nested claims should be separated by ".". | ``string`` | Yes |
|``match`` | the value of the claim to match against. | ``string`` | Yes |

### RateLimit.Condition.Variables

RateLimit.Condition.Variables defines a condition for a rate limit by NGINX variable. The following example defines a condition for a rate limit policy that only applies to requests with the request method with a value `GET`:

```yaml
variables:
  - name: $request_method
    match: GET
```

**Note:** 
Only one variable at a time is supported at present.

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``name`` | the name of the NGINX variable to be rate limit by. | ``string`` | Yes |
|``match`` | the value of the NGINX variable to match against.  Values prefixed with the `~` character denote the following is a [regular expression](https://nginx.org/en/docs/http/ngx_http_map_module.html#map).  | ``string`` | Yes |

### APIKey

The API Key auth policy configures NGINX to authorize client requests based on the presence of a valid API Key in a header or query param specified in the policy.

**Note:** 

The feature is implemented using NGINX [ngx_http_auth_request_module](http://nginx.org/en/docs/http/ngx_http_auth_request_module.html) and [NGINX JavaScript (NJS)](https://nginx.org/en/docs/njs/).

The policies' API keys are securely stored using SHA-256 hashing. When a client sends an API Key, it is hashed by NJS and then compared to the hashed API Key in the NGINX config.

If the hashed keys match, the NGINX JavaScript (NJS) subrequest issues a 204 No Content response to the `auth_request` directive, indicating successful authorization. Conversely, if no API Key is provided in the specified header or query parameter, a 401 Unauthorized response is returned. Similarly, if an invalid key is presented in the expected header or query parameter, a 403 Forbidden response is issued, denying access.

It is possible to use the [errorPages](/nic/configuration/virtualserver-and-virtualserverroute-resources.md#errorpage) property on a route, to change the default behaviour of 401 or 403 errors.

At least one header or query param is required.

The policy below configures NGINX Ingress Controller to require the API Key `password` in the header "my-header".

```yaml
apiKey:
    suppliedIn:
      header:
      - "my-header"
    clientSecret: api-key-secret
```

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: api-key-secret
type: nginx.org/apikey
data:
    client1: cGFzc3dvcmQ= # password
```

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``suppliedIn`` | `header` or `query`. | | Yes |
|``suppliedIn.header`` | An array of headers that the API Key may appear in. | ``string[]`` | No |
|``suppliedIn.query`` | An array of query params that the API Key may appear in. | ``string[]`` | No |
|``clientSecret`` | The name of the Kubernetes secret that stores the API Key(s). It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/apikey``, and the API Key(s) must be stored in a key: val format where each key is a unique clientID and each value is a unique base64 encoded API Key  | ``string`` | Yes |

**Note:** An APIKey Policy must include a minimum of one of the `suppliedIn.header` or `suppliedIn.query` parameters.  Both can also be supplied.

#### APIKey Merging Behavior

A VirtualServer or VirtualServerRoute can be associated with only one API Key policy per route or subroute. However, it is possible to replace an API Key policy from a higher-level with a different policy defined on a more specific route.

For example, a VirtualServer can implement different API Key policies at various levels. In the configuration below, the server-wide api-key-policy-server applies to /backend1 for authorization, as it lacks a more specific policy. Meanwhile, /backend2 uses the api-key-policy-route defined at the route level.

```yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: virtual-server
spec:
  host: virtual-server.example.com
  policies:
  - name: api-key-policy-server
  upstreams:
  - name: backend2
    service: backend2-svc
    port: 80
  - name: backend1
    service: backend1-svc
    port: 80
  routes:
  - path: /backend1
    action:
      pass: backend1
  - path: /backend2
    action:
      pass: backend2
    policies:
      - name: api-key-policy-route
```

### BasicAuth

The basic auth policy configures NGINX to authenticate client requests using the [HTTP Basic authentication scheme](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).

For example, the following policy will reject all requests that do not include a valid username/password combination in the HTTP header `Authentication`

```yaml
basicAuth:
  secret: htpasswd-secret
  realm: "My API"
```

**Note:** 
The feature is implemented using the NGINX [ngx_http_auth_basic_module](https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html).

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``secret`` | The name of the Kubernetes secret that stores the Htpasswd configuration. It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/htpasswd``, and the config must be stored in the secret under the key ``htpasswd``, otherwise the secret will be rejected as invalid. | ``string`` | Yes |
|``realm`` | The realm for the basic authentication. | ``string`` | No |

#### BasicAuth Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple basic auth policies. However, only one can be applied. Every subsequent reference will be ignored. For example, here we reference two policies:

```yaml
policies:
- name: basic-auth-policy-one
- name: basic-auth-policy-two
```

In this example NGINX Ingress Controller will use the configuration from the first policy reference `basic-auth-policy-one`, and ignores `basic-auth-policy-two`.

### JWT Using Local Kubernetes Secret

**Note:** 

This feature is only available with NGINX Plus.

The JWT policy configures NGINX Plus to authenticate client requests using JSON Web Tokens.

The following example policy will reject all requests that do not include a valid JWT in the HTTP header `token`:

```yaml
jwt:
  secret: jwk-secret
  realm: "My API"
  token: $http_token
```

You can pass the JWT claims and JOSE headers to the upstream servers. For example:

```yaml
action:
  proxy:
    upstream: webapp
    requestHeaders:
      set:
      - name: user
        value: ${jwt_claim_user}
      - name: alg
        value: ${jwt_header_alg}
```

We use the `requestHeaders` of the [Action.Proxy](/nic/configuration/virtualserver-and-virtualserverroute-resources.md#actionproxy) to set the values of two headers that NGINX will pass to the upstream servers.

The value of the `${jwt_claim_user}` variable is the `user` claim of a JWT. For other claims, use `${jwt_claim_name}`, where `name` is the name of the claim. Note that nested claims and claims that include a period (`.`) are not supported. Similarly, use `${jwt_header_name}` where `name` is the name of a header. In our example, we use the `alg` header.

**Note:** 

This feature is implemented using the NGINX Plus [ngx_http_auth_jwt_module](https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html).

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``secret`` | The name of the Kubernetes secret that stores the JWK. It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/jwk``, and the JWK must be stored in the secret under the key ``jwk``, otherwise the secret will be rejected as invalid. | ``string`` | Yes |
|``realm`` | The realm of the JWT. | ``string`` | Yes |
|``token`` | The token specifies a variable that contains the JSON Web Token. By default the JWT is passed in the ``Authorization`` header as a Bearer Token. JWT may be also passed as a cookie or a part of a query string, for example: ``$cookie_auth_token``. Accepted variables are ``$http_``, ``$arg_``, ``$cookie_``. | ``string`` | No |

#### JWT Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple JWT policies. However, only one can be applied: every subsequent reference will be ignored. For example, here we reference two policies:

```yaml
policies:
- name: jwt-policy-one
- name: jwt-policy-two
```

In this example NGINX Ingress Controller will use the configuration from the first policy reference `jwt-policy-one`, and ignores `jwt-policy-two`.

### JWT Using JWKS From Remote Location

**Note:** 

This feature is only available with NGINX Plus.

The JWT policy configures NGINX Plus to authenticate client requests using JSON Web Tokens, allowing import of the keys (JWKS) for JWT policy by means of a URL (for a remote server or an identity provider) as a result they don't have to be copied and updated to the IC pod.

The following example policy will reject all requests that do not include a valid JWT in the HTTP header fetched from the identity provider:

```yaml
jwt:
  realm: MyProductAPI
  token: $http_token
  jwksURI: <uri_to_remote_server_or_idp>
  keyCache: 1h
```

**Note:** 

This feature is implemented using the NGINX Plus directive [auth_jwt_key_request](http://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_key_request) under [ngx_http_auth_jwt_module](https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html).

|Field | Description | Type | Required | Default |
| ---| ---| ---| --- | --- |
|``jwksURI`` | The remote URI where the request will be sent to retrieve JSON Web Key set| ``string`` | Yes | -- |
|``keyCache`` | Enables in-memory caching of JWKS (JSON Web Key Sets) that are obtained from the ``jwksURI`` and sets a valid time for expiration. | ``string`` | Yes | -- |
|``realm`` | The realm of the JWT. | ``string`` | Yes | -- |
|``token`` | The token specifies a variable that contains the JSON Web Token. By default the JWT is passed in the ``Authorization`` header as a Bearer Token. JWT may be also passed as a cookie or a part of a query string, for example: ``$cookie_auth_token``. Accepted variables are ``$http_``, ``$arg_``, ``$cookie_``. | ``string`` | No | -- |
|``sniEnabled`` | Enables SNI (Server Name Indication) for the JWT policy. This is useful when the remote server requires SNI to serve the correct certificate. | ``bool`` | No | `false` |
|``sniName`` | The SNI name to use when connecting to the remote server. If not set, the hostname from the ``jwksURI`` will be used. | ``string`` | No | -- |
|``sslVerify`` | Enables verification of the JWKS server SSL certificate. | ``bool`` | No | `false` |
|``sslVerifyDepth`` | Sets the verification depth in the JWKS server certificates chain. | ``int`` | No | `1` |
|``trustedCertSecret`` | The name of the Kubernetes secret that stores the CA certificate for JWKS server verification. It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/ca``, and the certificate must be stored in the secret under the key ``ca.crt``. | ``string`` | No | -- |

**Note:** 

Content caching is enabled by default for each JWT policy with a default time of 12 hours.

This is done to ensure to improve resiliency by allowing the JWKS (JSON Web Key Set) to be retrieved from the cache even when it has expired.

#### JWT Merging Behavior

This behavior is similar to using a local Kubernetes secret where a VirtualServer/VirtualServerRoute can reference multiple JWT policies. However, only one can be applied: every subsequent reference will be ignored. For example, here we reference two policies:

```yaml
policies:
- name: jwt-policy-one
- name: jwt-policy-two
```

In this example NGINX Ingress Controller will use the configuration from the first policy reference `jwt-policy-one`, and ignores `jwt-policy-two`.

### IngressMTLS

The IngressMTLS policy configures client certificate verification.

For example, the following policy will verify a client certificate using the CA certificate specified in the `ingress-mtls-secret`:

```yaml
ingressMTLS:
  clientCertSecret: ingress-mtls-secret
  verifyClient: "on"
  verifyDepth: 1
```

Below is an example of the `ingress-mtls-secret` using the secret type `nginx.org/ca`

```yaml
kind: Secret
metadata:
  name: ingress-mtls-secret
apiVersion: v1
type: nginx.org/ca
data:
  ca.crt: <base64encoded-certificate>
```

A VirtualServer that references an IngressMTLS policy must:

- Enable [TLS termination](/nic/configuration/virtualserver-and-virtualserverroute-resources.md#virtualservertls).
- Reference the policy in the VirtualServer [`spec`](/nic/configuration/virtualserver-and-virtualserverroute-resources.md#virtualserver-specification). It is not allowed to reference an IngressMTLS policy in a [`route`]((/nic/configuration/virtualserver-and-virtualserverroute-resources.md#virtualserverroute) or in a VirtualServerRoute [`subroute`](/nic/configuration/virtualserver-and-virtualserverroute-resources.md#virtualserverroutesubroute).

A Kubernetes Ingress that references an IngressMTLS policy must:

- Enable [TLS termination](/nic/configuration/ingress-resources/advanced-configuration-with-annotations.md#auth-and-ssltls).
- Reference the policy on the Ingress. For [mergeable Ingresses](/nic/configuration/ingress-resources/custom-annotations.md#custom-annotations-with-mergeable-ingress-resources), the policy must be on the master Ingress only; referencing an IngressMTLS policy on a minion Ingress is not allowed.

If the conditions above are not met, NGINX will send the `500` status code to clients.

You can pass the client certificate details, including the certificate, to the upstream servers. For example:

```yaml
action:
  proxy:
    upstream: webapp
    requestHeaders:
      set:
      - name: client-cert-subj-dn
        value: ${ssl_client_s_dn} # subject DN
      - name: client-cert
        value: ${ssl_client_escaped_cert} # client certificate in the PEM format (urlencoded)
```

We use the `requestHeaders` of the [Action.Proxy](/nic/configuration/virtualserver-and-virtualserverroute-resources.md#actionproxy) to set the values of the two headers that NGINX will pass to the upstream servers. See the [list of embedded variables](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables) that are supported by the `ngx_http_ssl_module`, which you can use to pass the client certificate details.

**Note:** 

 The feature is implemented using the NGINX [ngx_http_ssl_module](https://nginx.org/en/docs/http/ngx_http_ssl_module.html).

 

#### Using a Certificate Revocation List

The IngressMTLS policy supports configuring at CRL for your policy.
This can be done in one of two ways.

**Note:** 

 Only one of these configurations options can be used at a time.

1. Adding the `ca.crl` field to the `nginx.org/ca` secret type, which accepts a base64 encoded certificate revocation list (crl).
   Example YAML:

```yaml
kind: Secret
metadata:
  name: ingress-mtls-secret
apiVersion: v1
type: nginx.org/ca
data:
  ca.crt: <base64encoded-certificate>
  ca.crl: <base64encoded-crl>
```

2. Adding the `crlFileName` field to your IngressMTLS policy spec with the name of the CRL file.

**Note:** 

This configuration option should only be used when using a CRL that is larger than 1MiB.

Otherwise we recommend using the `nginx.org/ca` secret type for managing your CRL.

Example YAML:

```yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: ingress-mtls-policy
spec:
ingressMTLS:
    clientCertSecret: ingress-mtls-secret
    crlFileName: webapp.crl
    verifyClient: "on"
    verifyDepth: 1
```

**Note:** 
When configuring a CRL with the `ingressMTLS.crlFileName` field, there is additional context to keep in mind:

1. NGINX Ingress Controller will expect the CRL, in this case `webapp.crl`, will be in `/etc/nginx/secrets`. A volume mount will need to be added to NGINX Ingress Controller deployment add your CRL to `/etc/nginx/secrets`
2. When updating the content of your CRL (e.g a new certificate has been revoked), NGINX will need to be reloaded to pick up the latest changes. Depending on your environment this may require updating the name of your CRL and applying this update to your `ingress-mtls.yaml` policy to ensure NGINX picks up the latest CRL.

Please refer to the Kubernetes documentation on [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) to find the best implementation for your environment.

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``clientCertSecret`` | The name of the Kubernetes secret that stores the CA certificate. It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/ca``, and the certificate must be stored in the secret under the key ``ca.crt``, otherwise the secret will be rejected as invalid. | ``string`` | Yes |
|``verifyClient`` | Verification for the client. Possible values are ``"on"``, ``"off"``, ``"optional"``, ``"optional_no_ca"``. The default is ``"on"``. | ``string`` | No |
|``verifyDepth`` | Sets the verification depth in the client certificates chain. The default is ``1``. | ``int`` | No |
|``crlFileName`` | The file name of the Certificate Revocation List. NGINX Ingress Controller will look for this file in `/etc/nginx/secrets` | ``string`` | No |

#### IngressMTLS Merging Behavior

A VirtualServer and an Ingress can reference only a single IngressMTLS policy. Every subsequent reference will be ignored. For example, here we reference two policies:

```yaml
policies:
- name: ingress-mtls-policy-one
- name: ingress-mtls-policy-two
```

In this example NGINX Ingress Controller will use the configuration from the first policy reference `ingress-mtls-policy-one`, and ignores `ingress-mtls-policy-two`.

### EgressMTLS

The EgressMTLS policy configures upstreams authentication and certificate verification.

For example, the following policy will use `egress-mtls-secret` to authenticate with the upstream application and `egress-trusted-ca-secret` to verify the certificate of the application:

```yaml
egressMTLS:
  tlsSecret: egress-mtls-secret
  trustedCertSecret: egress-trusted-ca-secret
  verifyServer: on
  verifyDepth: 2
```

**Note:** 

The feature is implemented using the NGINX [ngx_http_proxy_module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html).

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``tlsSecret`` | The name of the Kubernetes secret that stores the TLS certificate and key. It must be in the same namespace as the Policy resource. The secret must be of the type ``kubernetes.io/tls``, the certificate must be stored in the secret under the key ``tls.crt``, and the key must be stored under the key ``tls.key``, otherwise the secret will be rejected as invalid. | ``string`` | No |
|``trustedCertSecret`` | The name of the Kubernetes secret that stores the CA certificate. It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/ca``, and the certificate must be stored in the secret under the key ``ca.crt``, otherwise the secret will be rejected as invalid. | ``string`` | No |
|``verifyServer`` | Enables verification of the upstream HTTPS server certificate. | ``bool`` | No |
|``verifyDepth`` | Sets the verification depth in the proxied HTTPS server certificates chain. The default is ``1``. | ``int`` | No |
|``sessionReuse`` | Enables reuse of SSL sessions to the upstreams. The default is ``true``. | ``bool`` | No |
|``serverName`` | Enables passing of the server name through ``Server Name Indication`` extension. | ``bool`` | No |
|``sslName`` | Allows overriding the server name used to verify the certificate of the upstream HTTPS server. | ``string`` | No |
|``ciphers`` | Specifies the enabled ciphers for requests to an upstream HTTPS server. The default is ``DEFAULT``. | ``string`` | No |
|``protocols`` | Specifies the protocols for requests to an upstream HTTPS server. The default is ``TLSv1 TLSv1.1 TLSv1.2``. | ``string`` | No | > Note: the value of ``ciphers`` and ``protocols`` is not validated by NGINX Ingress Controller. As a result, NGINX can fail to reload the configuration. To ensure that the configuration for a VirtualServer/VirtualServerRoute that references the policy was successfully applied, check its [status](/nic/configuration/global-configuration/reporting-resources-status.md#virtualserver-and-virtualserverroute-resources). The validation will be added in the future releases. |

#### EgressMTLS Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple EgressMTLS policies. However, only one can be applied. Every subsequent reference will be ignored. For example, here we reference two policies:

```yaml
policies:
- name: egress-mtls-policy-one
- name: egress-mtls-policy-two
```

In this example NGINX Ingress Controller will use the configuration from the first policy reference `egress-mtls-policy-one`, and ignores `egress-mtls-policy-two`.

### ExternalAuth

The ExternalAuth policy configures NGINX to authenticate client requests using an external authentication server. You can use this policy with services such as [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/) or any custom authentication service that supports the `auth_request` pattern.

When a client sends a request, NGINX makes an internal subrequest to the external authentication service. If the service returns a `2xx` response, the original request is forwarded to the upstream. If it returns `401` or `403`, access is denied. If `authSigninURI` is configured, unauthenticated clients are redirected to a sign-in page.

For example, the following policy configures external authentication using an HTTP Basic Auth backend service:

```yaml
externalAuth:
  authURI: "/auth"
  authServiceName: "default/basic-auth-svc"
```

The following policy uses OAuth2 Proxy with a sign-in redirect:

```yaml
externalAuth:
  authURI: "/oauth2/auth"
  authSigninURI: "/oauth2/signin"
  authServiceName: "default/oauth2-proxy-svc"
  sslEnabled: true
  sslVerify: true
  sslVerifyDepth: 2
  sniName: "external-auth-tls"
  trustedCertSecret: "external-auth-ca-secret"
```

An example of an ExternalAuth policy for VirtualServer resources is available in our GitHub repository for [basic auth](https://github.com/nginx/kubernetes-ingress/blob/v/examples/custom-resources/external-auth) and [OAuth2 with basic auth](https://github.com/nginx/kubernetes-ingress/blob/v/examples/custom-resources/external-auth-oauth2). Examples for Ingress resources are also available for [basic auth](https://github.com/nginx/kubernetes-ingress/blob/v/examples/ingress-resources/external-auth) and [OAuth2 with basic auth using Mergeable Ingresses](https://github.com/nginx/kubernetes-ingress/blob/v/examples/ingress-resources/external-auth-mergeable).

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``authURI`` | The URI of the external authentication server. NGINX sends an internal subrequest to this URI to verify the client. Must start with ``/``. For example, ``/auth`` or ``/oauth2/auth``. | ``string`` | Yes |
|``authServiceName`` | The name of the Kubernetes service for the external authentication server. Can include an optional namespace prefix in the format ``<namespace>/<service>``. For example, ``basic-auth-svc`` or ``auth-namespace/auth-service``. If no namespace is specified, the namespace of the Policy resource is used. | ``string`` | Yes |
|``authServicePorts`` | The ports of the Kubernetes service to which authentication requests are sent. If not specified, the first port from the service definition is used. | ``[]int`` | No |
|``authSigninURI`` | The URI to redirect unauthenticated clients to for sign-in. Used when the external authentication server requires redirection, such as with OAuth2 Proxy. Must start with ``/``. For example, ``/oauth2/signin``. | ``string`` | No |
|``authSnippets`` | Custom NGINX configuration snippets to add to the external authentication location block. For example, you can add extra headers or parameters for the ``auth_request`` module. The content must be valid NGINX configuration. Requires the [``-enable-snippets``](/nic/configuration/global-configuration/command-line-arguments.md#cmdoption-enable-snippets) command-line argument. | ``string`` | No |
|``authSigninRedirectBasePath`` | The base path for the NGINX location block that handles sign-in redirect requests from the external authentication server. For example, OAuth2 Proxy expects ``/oauth2``. Defaults to ``/oauth2`` if not specified. | ``string`` | No |
|``sslEnabled`` | Enables HTTPS when proxying requests to the external authentication server. The default is ``false``. | ``bool`` | No |
|``sslVerify`` | Enables verification of the external authentication server's SSL certificate. The default is ``false``. | ``bool`` | No |
|``sslVerifyDepth`` | Sets the verification depth in the external authentication server certificates chain. The default is ``1``. | ``int`` | No |
|``trustedCertSecret`` | The name of the Kubernetes secret that stores the CA certificate for external authentication server certificate verification. Can include an optional namespace prefix as ``<namespace>/<secret>``. The secret must be of the type ``nginx.org/ca``, and the certificate must be stored under the key ``ca.crt``. | ``string`` | No |
|``sniName`` | The server name used for SNI and certificate verification when connecting to the external authentication server over TLS. If not specified, defaults to ``<service-name>.<namespace>.svc`` derived from ``authServiceName``. | ``string`` | No |

#### ExternalAuth Merging Behavior

A VirtualServer, VirtualServerRoute, Ingress, or Mergeable Ingress can reference only one ExternalAuth policy per route. Every subsequent reference will be ignored. This means you cannot combine different types of external authentication on the same route. For example, you cannot apply both an OAuth2 policy and a basic auth policy to the same route.

```yaml
policies:
- name: external-auth-policy-one
- name: external-auth-policy-two
```

NGINX Ingress Controller will use the configuration from the first policy reference `external-auth-policy-one`, and ignores `external-auth-policy-two`. To use different authentication methods on different routes, apply each ExternalAuth policy to its own route.

#### OAuth2 sign-in redirect location

When you configure `authSigninURI`, NGINX Ingress Controller generates an internal location block to handle sign-in redirects (based on `authSigninRedirectBasePath`, which defaults to `/oauth2`). Because NGINX location blocks are defined at the server level, only one sign-in redirect location can exist per host. If multiple routes on the same VirtualServer, VirtualServerRoute, Ingress, or Mergeable Ingress host reference ExternalAuth policies with `authSigninURI`, NGINX Ingress Controller uses the sign-in redirect configuration from the first policy it processes. All routes that use `authSigninURI` share that single redirect location.

This means all routes on the same host that require OAuth2 sign-in must use the same OAuth2 Proxy backend for the redirect flow. If you need different OAuth2 providers for different routes, use separate hosts.

### OIDC

**Note:** 

This feature is disabled by default. To enable it, set the [enable-oidc](/nic/configuration/global-configuration/command-line-arguments.md#cmdoption-enable-oidc) command-line argument of NGINX Ingress Controller.

The OIDC policy configures NGINX Plus as a relying party for OpenID Connect authentication.

For example, the following policy will use the client ID `nginx-plus` and the client secret `oidc-secret` to authenticate with the OpenID Connect provider `https://idp.example.com`:

```yaml
spec:
  oidc:
    clientID: nginx-plus
    clientSecret: oidc-secret
    authEndpoint: https://idp.example.com/openid-connect/auth
    tokenEndpoint: https://idp.example.com/openid-connect/token
    jwksURI: https://idp.example.com/openid-connect/certs
    endSessionEndpoint: https://idp.example.com/openid-connect/logout
    postLogoutRedirectURI: /
    accessTokenEnable: true
    pkceEnable: false
```

NGINX Plus will pass the ID of an authenticated user to the backend in the HTTP header `username`.

**Note:** 

The feature is implemented using the [reference implementation](https://github.com/nginxinc/nginx-openid-connect/) of NGINX Plus as a relying party for OpenID Connect authentication.

#### Prerequisites

In order to use OIDC, you need to enable [zone synchronization](/nginx/admin-guide/high-availability/zone_sync.md). If you don't set up zone synchronization, NGINX Plus will fail to reload.
You also need to configure a resolver, which NGINX Plus will use to resolve the IDP authorization endpoint. You can find an example configuration [in our GitHub repository](https://github.com/nginx/kubernetes-ingress/blob/v/examples/custom-resources/oidc#step-7---configure-nginx-plus-zone-synchronization-and-resolver).

**Note:** 

The configuration in the example doesn't enable TLS and the synchronization between the replica happens in clear text. This could lead to the exposure of tokens.

#### Limitations

The OIDC policy defines a few internal locations that can't be customized: `/_jwks_uri`, `/_token`, `/_refresh`, `/_id_token_validation`, `/logout`. In addition, as explained below, `/_codexch` is the default value for redirect URI, and `/_logout` is the default value for post logout redirect URI, both of which can be customized. Specifying one of these locations as a route in the VirtualServer or VirtualServerRoute will result in a collision and NGINX Plus will fail to reload.

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``clientID`` | The client ID provided by your OpenID Connect provider. | ``string`` | Yes |
|``clientSecret`` | The name of the Kubernetes secret that stores the client secret provided by your OpenID Connect provider. It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/oidc``, and the secret under the key ``client-secret``, otherwise the secret will be rejected as invalid. If PKCE is enabled, this should be not configured. | ``string`` | Yes |
|``authEndpoint`` | URL for the authorization endpoint provided by your OpenID Connect provider. | ``string`` | Yes |
|``authExtraArgs`` | A list of extra URL arguments to pass to the authorization endpoint provided by your OpenID Connect provider. Arguments must be URL encoded, multiple arguments may be included in the list, for example ``[ arg1=value1, arg2=value2 ]`` | ``string[]`` | No |
|``tokenEndpoint`` | URL for the token endpoint provided by your OpenID Connect provider. | ``string`` | Yes |
|``endSessionEndpoint`` | URL provided by your OpenID Connect provider to request the end user be logged out. | ``string`` | No |
|``jwksURI`` | URL for the JSON Web Key Set (JWK) document provided by your OpenID Connect provider. | ``string`` | Yes |
|``scope`` | List of OpenID Connect scopes. The scope ``openid`` always needs to be present and others can be added concatenating them with a ``+`` sign, for example ``openid+profile+email``, ``openid+email+userDefinedScope``. The default is ``openid``. | ``string`` | No |
|``redirectURI`` | Allows overriding the default redirect URI. The default is ``/_codexch``. | ``string`` | No |
|``postLogoutRedirectURI`` | URI to redirect to after the logout has been performed. Requires ``endSessionEndpoint``. The default is ``/_logout``. | ``string`` | No |
|``zoneSyncLeeway`` | Specifies the maximum timeout in milliseconds for synchronizing ID/access tokens and shared values between Ingress Controller pods. The default is ``200``. | ``int`` | No |
|``accessTokenEnable`` | Option of whether Bearer token is used to authorize NGINX to access protected backend. | ``boolean`` | No |
|``pkceEnable`` | Switches Proof Key for Code Exchange on. The OpenID client needs to be in public mode. `clientSecret` is not used in this mode. | ``boolean`` | No |
|``sslVerify`` | Use this option to enable TLS verification when calls are made to the IDP endpoints. | ``boolean`` | No |
|``verifyDepth`` | Sets the verification depth in the proxied HTTPS server certificates chain. The default is ``1``. | ``int`` | No |
|``trustedCertSecret`` | The name of the Kubernetes secret that stores the CA certificate. It must be in the same namespace as the Policy resource. The secret must be of the type ``nginx.org/ca``, and the certificate must be stored in the secret under the key ``ca.crt``, otherwise the secret will be rejected as invalid. | ``string`` | No |

**Note:** 

Only one OIDC policy can be referenced in a VirtualServer and its VirtualServerRoutes. However, the same policy can still be applied to different routes in the VirtualServer and VirtualServerRoutes.

#### OIDC Merging Behavior

A VirtualServer/VirtualServerRoute can reference only a single OIDC policy. Every subsequent reference will be ignored. For example, here we reference two policies:

```yaml
policies:
- name: oidc-policy-one
- name: oidc-policy-two
```

In this example NGINX Ingress Controller will use the configuration from the first policy reference `oidc-policy-one`, and ignores `oidc-policy-two`.

### Cache

The cache policy configures proxy caching, which improves performance by storing and serving cached responses to clients without having to proxy every request to upstream servers.

For example, the following policy creates a cache zone named "my-cache" with 10MB memory allocation and caches all GET response codes for 30 seconds:

```yaml
cache:
  cacheZoneName: "mycache"
  cacheZoneSize: "10m"
  allowedCodes: ["any"]
  allowedMethods: ["GET"]
  time: "30s"
```

Here's an example with more specific configuration:

```yaml
cache:
  cacheZoneName: "mycache"
  cacheZoneSize: "100m"
  allowedCodes: [200, 301, 302]
  allowedMethods: ["GET", "POST"]
  time: "5m"
  levels: "1:2"
  overrideUpstreamCache: true
  inactive: "60m"
  useTempPath: false
  maxSize: "10g"
  minFree: "1g"
  manager:
    files: 100
    sleep: "50ms"
    threshold: "200ms"
  cacheKey: "$scheme$host$request_uri"
  cacheUseStale: [ "error", "timeout", "updating", "http_500" ]
  cacheRevalidate: true
  cacheBackgroundUpdate: true
  cacheMinUses: 1
  lock:
    enable: true
    timeout: "5s"
    age: "30s"
  conditions:
    noCache: [ "$cookie_nocache", "$arg_nocache" ]
    bypass: [ "$http_authorization" ]
```

**Note:** 

The feature is implemented using the NGINX [ngx_http_proxy_module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_path) proxy_cache_path and related directives.

|Field | Description | Type | Required |
| --- | ---| ---| --- |
|``cacheZoneName`` | CacheZoneName defines the name of the cache zone. Must start with a lowercase letter,followed by alphanumeric characters or underscores, and end with an alphanumeric character. Single lowercase letters are also allowed. Examples: "cache", "my_cache", "cache1". | ``string`` | Yes |
|``cacheZoneSize`` | CacheZoneSize defines the size of the cache zone. Must be a number followed by a size unit: 'k' for kilobytes, 'm' for megabytes, or 'g' for gigabytes. Examples: "10m", "1g", "512k". | ``string`` | Yes |
|``allowedCodes`` | AllowedCodes defines which HTTP response codes should be cached. Accepts either: - The string "any" to cache all response codes (must be the only element) - A list of HTTP status codes as integers (100-599) Examples: ["any"], [200, 301, 404], [200]. Invalid: ["any", 200] (cannot mix "any" with specific codes). | ``[]IntOrString`` | No |
|``time`` | The default cache time for responses. Required when allowedCodes is specified. Must be a number followed by a time unit: 's' for seconds, 'm' for minutes, 'h' for hours, 'd' for days. Examples: "30s", "5m", "1h", "2d". | ``string`` | No |
|``allowedMethods`` | AllowedMethods defines which HTTP methods should be cached. Only "GET", "HEAD", and "POST" are supported by NGINX proxy_cache_methods directive. GET and HEAD are always cached by default even if not specified. Maximum of 3 items allowed. Examples: ["GET"], ["GET", "HEAD", "POST"]. Invalid methods: PUT, DELETE, PATCH, etc.  | ``[]string`` | No |
|``levels`` | Levels defines the cache directory hierarchy levels for storing cached files. Must be in format "X:Y" or "X:Y:Z" where X, Y, Z are either 1 or 2. This controls the number of subdirectory levels and their name lengths. Examples: "1:2", "2:2", "1:2:2". Invalid: "3:1", "1:3", "1:2:3". | ``string`` | No |
|``overrideUpstreamCache`` | OverrideUpstreamCache controls whether to override upstream cache headers (using proxy_ignore_headers directive). When true, NGINX will ignore cache-related headers from upstream servers like Cache-Control, Expires etc, Default: false. | ``bool`` | No |
|``cachePurgeAllow`` | CachePurgeAllow defines IP addresses or CIDR blocks allowed to purge cache. This feature is only available in NGINX Plus. Examples: ["192.168.1.100", "10.0.0.0/8", "::1"]. | ``[]string`` | No |
|``cacheKey`` | CacheKey defines a key for caching (proxy_cache_key). By default, "$scheme$proxy_host$uri". Must not contain command execution patterns: $(, `, ;, &&, || | ``string`` | No |
|``cacheUseStale`` | CacheUseStale determines in which cases a stale cached response can be used (proxy_cache_use_stale). Valid parameters: error, timeout, invalid_header, updating, http_500, http_502, http_503, http_504, http_403, http_404, http_429, off. | ``[]string`` | No |
|``cacheRevalidate`` | CacheRevalidate enables revalidation of expired cache items using conditional requests (proxy_cache_revalidate). Uses "If-Modified-Since" and "If-None-Match" header fields. | ``bool`` | No |
|``cacheBackgroundUpdate`` | CacheBackgroundUpdate allows starting a background subrequest to update an expired cache item (proxy_cache_background_update). A stale cached response is returned to the client while the cache is being updated. | ``bool`` | No |
|``cacheMinUses`` | CacheMinUses sets the number of requests after which the response will be cached (proxy_cache_min_uses). | ``integer`` | No |
|``inactive`` | Inactive sets the time after which cached data that are not accessed get removed from the cache (inactive parameter). By default, inactive is set to 10 minutes. | ``string`` | No |
|``maxSize`` | MaxSize sets the maximum cache size (max_size parameter). When the size is exceeded, the cache manager removes the least recently used data. | ``string`` | No |
|``minFree`` | MinFree sets the minimum amount of free space required on the file system with cache (min_free parameter). When there is not enough free space, the cache manager removes the least recently used data. | ``string`` | No |
|``useTempPath`` | UseTempPath controls whether temporary files and the cache are put on different file systems (use_temp_path parameter). If set to false, temporary files will be put directly in the cache directory (use_temp_path=off). Default: false (use_temp_path=off, which puts temp files directly in cache directory for better performance). | ``bool`` | No |
|``manager`` | Manager configures the cache manager process parameters (manager_files, manager_sleep, manager_threshold). | ``object`` | No |
|``manager.files`` | Files sets the maximum number of files that will be deleted in one iteration by the cache manager. During one iteration no more than manager_files items are deleted (by default, 100). | ``integer`` | No |
|``manager.sleep`` | Sleep sets the pause between cache manager iterations. Between iterations, a pause configured by manager_sleep (by default, 50 milliseconds) is made. | ``string`` | No |
|``manager.threshold`` | Threshold sets the maximum duration of one cache manager iteration. The duration of one iteration is limited by manager_threshold (by default, 200 milliseconds). | ``string`` | No |
|``lock`` | Lock configures cache locking to prevent multiple identical requests from populating the same cache element simultaneously. | ``object`` | No |
|``lock.enable`` | Enable sets whether cache locking is enabled (proxy_cache_lock). When enabled, only one request at a time will be allowed to populate a new cache element according to the proxy_cache_key. | ``bool`` | No |
|``lock.timeout`` | Timeout sets a timeout for proxy_cache_lock. When the time expires, the request will be passed to the proxied server, however, the response will not be cached. | ``string`` | No |
|``lock.age`` | Age sets the maximum time a cache lock can be held (proxy_cache_lock_age). If the last request passed to the proxied server for populating a new cache element has not completed for the specified time, one more request may be passed. | ``string`` | No |
|``conditions`` | Conditions defines when responses should not be cached or taken from cache. | ``object`` | No |
|``conditions.noCache`` | NoCache defines conditions under which the response will not be saved to a cache (proxy_no_cache). If at least one value of the string parameters is not empty and is not equal to "0" then the response will not be saved. | ``[]string`` | No |
|``conditions.bypass`` | Bypass defines conditions under which the response will not be taken from a cache (proxy_cache_bypass). If at least one value of the string parameters is not empty and is not equal to "0" then the response will not be taken from the cache. | ``[]string`` | No |

#### Cache Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple cache policies. However, only one can be applied: every subsequent reference will be ignored.

### CORS

The CORS policy configures Cross-Origin Resource Sharing headers.

**Note:** The feature is implemented using the NGINX `add_header` directive.

Below is an example of a CORS policy configuring all the available options:

```yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: cors-policy
spec:
  cors:
    allowOrigin:
      - "https://test.example.com"
      - "https://app.example.com"
      - "https://admin.example.com"

    allowMethods:
      - "GET"
      - "POST"
      - "PUT"

    allowHeaders:
      - "Content-Type"
      - "Authorization"
      - "X-Requested-With"
      - "X-API-Key"

    allowCredentials: true

    exposeHeaders:
      - "X-Total-Count"
      - "X-Page-Size"
      - "X-RateLimit-Remaining"
      - "X-RateLimit-Reset"

    maxAge: 3600 
    
```

|Field | Description | Type | Required |
| --- | ---| ---| --- |
|``allowOrigin`` | AllowOrigin defines the origins that are allowed to make cross-origin requests. Can be exact domains, single wildcards, or `*` for all origins. Examples: ["https://example.com", "https://*.mydomain.com", "*"] Security: When allowCredentials is true, wildcard "*" is not allowed. The server must specify explicit origins for credentialed requests. |``array[string]`` | Yes |
|``allowMethods`` | AllowMethods defines the HTTP methods that are allowed for cross-origin requests. | ``array[string]`` | No |
|``allowHeaders`` | AllowHeaders defines the headers that are allowed in cross-origin requests. Common safe headers: ["Accept", "Accept-Language", "Content-Language", "Content-Type"] Custom headers: ["Authorization", "X-Requested-With", "X-Custom-Header"] |  ``array[string]`` | No |
|``allowCredentials`` | AllowCredentials indicates whether the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether the actual request can be made using credentials. | ``boolean`` | No |
|``exposeHeaders`` |  ExposeHeaders defines the headers that browsers are allowed to access. Use this field to expose additional custom headers to the browser. Example: ["X-Total-Count", "X-Page-Size", "X-RateLimit-Remaining"] Note: Set-Cookie headers cannot be exposed via CORS per official MDN specification. | ``array[string]`` | No |
|``maxAge`` |  MaxAge defines how long (in seconds) the results of a preflight request can be cached. Default: 86400 (24 hours). | ``integer`` | No |

**Note:** 
If CORS is currently configured in deployments using `snippets` or `responseHeaders.add`, migrate over the same settings to the CORS policy and remove the duplicate configuration.

#### CORS Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple CORS policies. However, only one can be applied: every subsequent reference will be ignored.

### WAF

**Note:**  The feature is implemented using the NGINX Plus [F5 WAF for NGINX module](/waf/). 

The WAF policy configures NGINX Plus to secure client requests using F5 WAF for NGINX policies.

For example, the following policy will enable the referenced APPolicy. You can configure multiple APLogConfs with log destinations:

```yaml
waf:
  enable: true
  apPolicy: "default/dataguard-alarm"
  securityLogs:
  - enable: true
    apLogConf: "default/logconf"
    logDest: "syslog:server=syslog-svc.default:514"
  - enable: true
    apLogConf: "default/logconf"
    logDest: "syslog:server=syslog-svc-secondary.default:514"
```

**Note:**  The field `waf.securityLog` is deprecated and will be removed in future releases. It will be ignored if `waf.securityLogs` is populated. 

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``enable`` | Enables F5 WAF for NGINX. | ``bool`` | Yes |
|``apPolicy`` | The [F5 WAF for NGINX policy](/nic/integrations/app-protect-waf/configuration.md#waf-policies) of the WAF. Accepts an optional namespace. Mutually exclusive with ``apBundle``. | ``string`` | No |
|``apBundle`` | The [F5 WAF for NGINX policy bundle](/nic/integrations/app-protect-waf/configuration.md#waf-bundles). Mutually exclusive with ``apPolicy``. | ``string`` | No |
|``securityLog.enable`` | **Deprecated:** Enables security log. | ``bool`` | No |
|``securityLog.apLogConf`` | **Deprecated:** The [F5 WAF for NGINX log conf](/nic/integrations/app-protect-waf/configuration.md#waf-logs) resource. Accepts an optional namespace. Only works with ``apPolicy``. | ``string`` | No |
|``securityLog.apLogBundle`` | **Deprecated:** The [F5 WAF for NGINX log bundle](/nic/integrations/app-protect-waf/configuration.md#waf-bundles) resource. Only works with ``apBundle``. | ``string`` | No |
|``securityLog.logDest`` | **Deprecated:** The log destination for the security log. Only accepted variables are ``syslog:server=<ip-address>; localhost; <fqdn>:<port>``, ``stderr``, ``<absolute path to file>``. | ``string`` | No |
|``securityLogs`` | Config for security log destinations. | [waf.securityLogs](#wafsecurityLogs) | No |

#### WAF.SecurityLogs

|Field | Description | Type | Required |
| ---| ---| ---| --- |
|``enable`` | Enables security log. | ``bool`` | No |
|``apLogConf`` | The [App Protect WAF log conf](/nic/integrations/app-protect-waf/configuration.md#waf-logs) resource. Accepts an optional namespace. Only works with ``apPolicy``. | ``string`` | No |
|``apLogBundle`` | The [App Protect WAF log bundle](/nic/integrations/app-protect-waf/configuration.md#waf-bundles) resource. Only works with ``apBundle``. | ``string`` | No |
|``logDest`` | The log destination for the security log. Only accepted variables are ``syslog:server=<ip-address>; localhost; <fqdn>:<port>``, ``stderr``, ``<absolute path to file>``. | ``string`` | No |

#### WAF Merging Behavior

A VirtualServer/VirtualServerRoute can reference multiple WAF policies. However, only one can be applied. Every subsequent reference will be ignored. For example, here we reference two policies:

```yaml
policies:
- name: waf-policy-one
- name: waf-policy-two
```

In this example NGINX Ingress Controller will use the configuration from the first policy reference `waf-policy-one`, and ignores `waf-policy-two`.

## Using Policy

You can use the usual `kubectl` commands to work with Policy resources, just as with built-in Kubernetes resources.

For example, the following command creates a Policy resource defined in `access-control-policy-allow.yaml` with the name `webapp-policy`:

```shell
kubectl apply -f access-control-policy-allow.yaml

policy.k8s.nginx.org/webapp-policy configured
```

You can get the resource by running:

```shell
kubectl get policy webapp-policy

NAME            AGE
webapp-policy   27m
```

For `kubectl get` and similar commands, you can also use the short name `pol` instead of `policy`.

### Applying Policies

You can apply policies to VirtualServer, VirtualServerRoute, and Ingress resources. For example:

- VirtualServer:

    ```yaml
    apiVersion: k8s.nginx.org/v1
    kind: VirtualServer
    metadata:
      name: cafe
      namespace: cafe
    spec:
      host: cafe.example.com
      tls:
        secret: cafe-secret
      policies: # spec policies
      - name: policy1
      upstreams:
      - name: coffee
        service: coffee-svc
        port: 80
      routes:
      - path: /tea
        policies: # route policies
        - name: policy2
          namespace: cafe
        route: tea/tea
      - path: /coffee
        policies: # route policies
        - name: policy3
          namespace: cafe
        action:
          pass: coffee
      ```

      For VirtualServer, you can apply a policy:
  * to all routes (spec policies)
  * to a specific route (route policies)

      Route policies of the *same type* override spec policies. In the example above, if the type of the policies `policy-1` and `policy-3` is `accessControl`, then for requests to `cafe.example.com/coffee`, NGINX will apply `policy-3`.

      The overriding is enforced by NGINX: the spec policies are implemented in the `server` context of the config, and the route policies are implemented in the `location` context. As a result, the route policies of the same type win.

- VirtualServerRoute, which is referenced by the VirtualServer above:

    ```yaml
    apiVersion: k8s.nginx.org/v1
    kind: VirtualServerRoute
    metadata:
      name: tea
      namespace: tea
    spec:
      host: cafe.example.com
      upstreams:
      - name: tea
        service: tea-svc
        port: 80
      subroutes: # subroute policies
      - path: /tea
        policies:
        - name: policy4
          namespace: tea
        action:
          pass: tea
    ```

    For VirtualServerRoute, you can apply a policy to a subroute (subroute policies).

    Subroute policies of the same type override spec policies. In the example above, if the type of the policies `policy-1` (in the VirtualServer) and `policy-4` is `accessControl`, then for requests to `cafe.example.com/tea`, NGINX will apply `policy-4`. As with the VirtualServer, the overriding is enforced by NGINX.

    Subroute policies always override route policies no matter the types. For example, the policy `policy-2` in the VirtualServer route will be ignored for the subroute `/tea`, because the subroute has its own policies (in our case, only one policy `policy4`). If the subroute didn't have any policies, then the `policy-2` would be applied. This overriding is enforced by NGINX Ingress Controller -- the `location` context for the subroute will either have route policies or subroute policies, but not both.

- Ingress:

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

    For Ingress, you can apply policies:
  * to a single Ingress
  * to a master Ingress, where minion Ingresses inherit the policies
  * to minion Ingresses, where minion policies override master policies

### Invalid Policies

NGINX will treat a policy as invalid if one of the following conditions is met:

- The policy doesn't pass the [comprehensive validation](#comprehensive-validation).
- The policy isn't present in the cluster.
- The policy doesn't meet its type-specific requirements. For example, an `ingressMTLS` policy requires TLS termination enabled in the VirtualServer.

For an invalid policy, NGINX returns the 500 status code for client requests with the following rules:

- If a policy is referenced in a VirtualServer `route` or a VirtualServerRoute `subroute`, then NGINX will return the 500 status code for requests for the URIs of that route/subroute.
- If a policy is referenced in the VirtualServer `spec`, then NGINX will return the 500 status code for requests for all URIs of that VirtualServer.

If a policy is invalid, the VirtualServer or VirtualServerRoute will have the [status](/nic/configuration/global-configuration/reporting-resources-status.md#virtualserver-and-virtualserverroute-resources) with the state `Warning` and the message explaining why the policy wasn't considered invalid.

### Validation

Two types of validation are available for the Policy resource:

- *Structural validation*, done by `kubectl` and the Kubernetes API server.
- *Comprehensive validation*, done by NGINX Ingress Controller.

#### Structural Validation

The custom resource definition for the Policy includes a structural OpenAPI schema, which describes the type of every field of the resource.

If you try to create (or update) a resource that violates the structural schema -- for example, the resource uses a string value instead of an array of strings in the `allow` field -- `kubectl` and the Kubernetes API server will reject the resource.

- Example of `kubectl` validation:

    ```shell
    kubectl apply -f access-control-policy-allow.yaml

    error: error validating "access-control-policy-allow.yaml": error validating data: ValidationError(Policy.spec.accessControl.allow): invalid type for org.nginx.k8s.v1.Policy.spec.accessControl.allow: got "string", expected "array"; if you choose to ignore these errors, turn validation off with --validate=false
    ```

- Example of Kubernetes API server validation:

    ```shell
    kubectl apply -f access-control-policy-allow.yaml --validate=false

    The Policy "webapp-policy" is invalid: spec.accessControl.allow: Invalid value: "string": spec.accessControl.allow in body must be of type array: "string"
    ```

If a resource passes structural validation, then NGINX Ingress Controller's comprehensive validation runs.

#### Comprehensive Validation

NGINX Ingress Controller validates the fields of a Policy resource. If a resource is invalid, NGINX Ingress Controller will reject it. The resource will continue to exist in the cluster, but NGINX Ingress Controller will ignore it.

You can use `kubectl` to check whether or not NGINX Ingress Controller successfully applied a Policy configuration. For our example `webapp-policy` Policy, we can run:

```shell
kubectl describe pol webapp-policy

. . .
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  11s   nginx-ingress-controller  Policy default/webapp-policy was added or updated
```

Note how the events section includes a Normal event with the AddedOrUpdated reason that informs us that the configuration was successfully applied.

If you create an invalid resource, NGINX Ingress Controller will reject it and emit a Rejected event. For example, if you create a Policy `webapp-policy` with an invalid IP `10.0.0.` in the `allow` field, you will get:

```shell
kubectl describe policy webapp-policy

. . .
Events:
  Type     Reason    Age   From                      Message
  ----     ------    ----  ----                      -------
  Warning  Rejected  7s    nginx-ingress-controller  Policy default/webapp-policy is invalid and was rejected: spec.accessControl.allow[0]: Invalid value: "10.0.0.": must be a CIDR or IP
```

Note how the events section includes a Warning event with the Rejected reason.

Additionally, this information is also available in the `status` field of the Policy resource. Note the Status section of the Policy:

```shell
kubectl describe pol webapp-policy

. . .
Status:
  Message:  Policy default/webapp-policy is invalid and was rejected: spec.accessControl.allow[0]: Invalid value: "10.0.0.": must be a CIDR or IP
  Reason:   Rejected
  State:    Invalid
```

**Note**: If you make an existing resource invalid, NGINX Ingress Controller will reject it.

