Repository: kubernetes/ingress-nginx
Pull request #7863 (2021-11-01 09:11:26)

Fix typos in ISSUE_TRIAGE.md
Signed-off-by: Hu Shuai <hus.fnst@cn.fujitsu.com>

<!--- Provide a general summary of your changes in the Title above --->
<!--- Please don't @-mention people in PR or commit messages (do so in an additional comment). --->

## What this PR does / why we need it:
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
Fix typos in ISSUE_TRIAGE.md:
neccessary --> necessary
priorization --> prioritization
upcomming --> upcoming
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [x] Documentation only

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [x] I've read the [CONTRIBUTION](https://github.com/kubernetes/ingress-nginx/blob/main/CONTRIBUTING.md) guide
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.


k8s-ci-robot: Welcome @hs0210! <br><br>It looks like this is your first PR to <a href='https://github.com/kubernetes/ingress-nginx'>kubernetes/ingress-nginx</a> 🎉. Please refer to our [pull request process documentation](https://git.k8s.io/community/contributors/guide/pull-requests.md) to help your PR have a smooth ride to approval. <br><br>You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. [Here is the bot commands documentation](https://go.k8s.io/bot-commands). <br><br>You can also check if kubernetes/ingress-nginx has [its own contribution guidelines](https://github.com/kubernetes/ingress-nginx/tree/master/CONTRIBUTING.md). <br><br>You may want to refer to our [testing guide](https://git.k8s.io/community/contributors/devel/sig-testing/testing.md) if you run into trouble with your tests not passing. <br><br>If you are having difficulty getting your pull request seen, please follow the [recommended escalation practices](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#why-is-my-pull-request-not-getting-reviewed). Also, for tips and tricks in the contribution process you may want to read the [Kubernetes contributor cheat sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md). We want to make sure your contribution gets all the attention it needs! <br><br>Thank you, and welcome to Kubernetes. :smiley:
k8s-ci-robot: @hs0210: This issue is currently awaiting triage.

If Ingress contributors determines this is a relevant issue, they will accept it by applying the `triage/accepted` label and provide further guidance.

The `triage/accepted` label can be added by org members by writing `/triage accepted` in a comment.


<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
k8s-ci-robot: Hi @hs0210. Thanks for your PR.

I'm waiting for a [kubernetes](https://github.com/orgs/kubernetes/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fingress-nginx).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/ingress-nginx/pull/7863#" title="Author self-approved">hs0210</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **dcbw** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @dcbw` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fingress-nginx).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes/ingress-nginx/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["dcbw"]} -->

Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index c39d6e2c2..63a7bfbf6 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -100,7 +100,7 @@ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main
 ## Create an ingress (please add any additional annotation required)
 
 echo "
-  apiVersion: networking.k8s.io/v1beta1
+  apiVersion: networking.k8s.io/v1
   kind: Ingress
   metadata:
     name: foo-bar
@@ -109,10 +109,13 @@ echo "
     - host: foo.bar
       http:
         paths:
-        - backend:
-            serviceName: http-svc
-            servicePort: 80
-          path: /
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+              name: http-svc
+              port: 
+                number: 80
 " | kubectl apply -f -
 
 ## make a request
diff --git a/ISSUE_TRIAGE.md b/ISSUE_TRIAGE.md
index 3a5ba48a9..e6d2c1d8e 100644
--- a/ISSUE_TRIAGE.md
+++ b/ISSUE_TRIAGE.md
@@ -18,9 +18,9 @@ This section describes the different stages of the triage flow for issues.
 New issues come in with the labels `needs-triage` and `needs-priority` and one of: `kind/bug`, `kind/feature` or `kind/support`. Unfortunately there are also some legacy issues that only have a `kind/*` label but neither `needs-triage` nor `needs-priority` . However for every issue that does not have the `triage-accepted` label the following steps have to be done to prepare them for further processing:  
 
 * Filter for issues [without the `triage-accepted`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+-label%3Atriage%2Faccepted+is%3Aissue) label.
-* Check if all neccessary information are available. This is basically true, if people filled out the issue template correctly. If neccessary information is missing, ask the author to add the missing information and add the label `triage/needs-information` if not already present. If already present, send the author a friendly reminder to add those. 
+* Check if all necessary information are available. This is basically true, if people filled out the issue template correctly. If necessary information is missing, ask the author to add the missing information and add the label `triage/needs-information` if not already present. If already present, send the author a friendly reminder to add those. 
 * Check if the used versions of ingress-nginx and Kubernetes is supported. Note that [we only support n-3 versions](https://github.com/kubernetes/ingress-nginx#support-versions-table). If the version is not supported, ask the author to upgrade to newer versions and see if the error still persists. 
-* Read through the issue description and comments briefly to understand what the issue is about. Also check if the kind and area is correct, and adjust it if neccessary. If the issue is understandable add the label `triage-accepted`.
+* Read through the issue description and comments briefly to understand what the issue is about. Also check if the kind and area is correct, and adjust it if necessary. If the issue is understandable add the label `triage-accepted`.
 * If at any point you don't know how to proceed with an issue during the triage process, tag one of the [core maintainers](OWNERS_ALIASES) in the issue to raise attention or alternatively come to [this slack channel](https://kubernetes.slack.com/archives/C021E147ZA4) which may be the quicker way as people tend to miss github notifications. 
 
 Note: Issues that are stale for 90 days are being closed automatically. However we could be missing a bug here, so from time to time it makes sense to go over the closed ones and see if there is something important. Use [this filter](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aclosed+is%3Aissue+label%3Alifecycle%2Frotten+) to find those. 
@@ -28,8 +28,8 @@ Note: Issues that are stale for 90 days are being closed automatically. However
 Who and When?
 * Basically everyone who wants to contribute can do the mentioned steps at any time.
 
-### Issue Priorization
-For all issues, where all neccessary information is available thus triage is accepted, we need to do some priorization: 
+### Issue Prioritization
+For all issues, where all necessary information is available thus triage is accepted, we need to do some prioritization: 
 
 * Go through all issues with label [`triage-accepted`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+is%3Aissue+label%3Atriage%2Faccepted+).  
 * Add appropriate priority label: `priority/backlog`, `priority/critical-urgent`, `priority/awaiting-more-evidence`, `priority/important-longterm`, `priority/important-soon` or `good first issue`
@@ -46,14 +46,14 @@ This section describes the different stages of the triage flow for pull requests
 Pull requests come in with the labels `needs-triage`, `needs-priority` and `needs-kind` and one that indicates the size(`size/*`). Unfortunately there are also some legacy pull requests that only have a `size/*` label but neither `needs-triage` nor `needs-priority` . However for every pull request that does not have the `triage-accepted` label the following steps should be done to prepare them for further processing:   
 
 * Filter for pull requests [without the `triage-accepted`](https://github.com/kubernetes/ingress-nginx/pulls?q=is%3Aopen+-label%3Atriage%2Faccepted+is%3Apr) label.
-* Check if the cla is signed and all neccessary information are available. This is basically true, if people filled out the pull request template correctly. If everything is fine add the `triage-accepted` label.
+* Check if the cla is signed and all necessary information are available. This is basically true, if people filled out the pull request template correctly. If everything is fine add the `triage-accepted` label.
 * If at any point you don't know how to proceed with an issue during the triage process, tag one of the [core maintainers](OWNERS_ALIASES) in the issue to raise attention or alternatively come to [this slack channel](https://kubernetes.slack.com/archives/C021E147ZA4) which may be the quicker way as people tend to miss github notifications. 
 
 Who and When?
 * Basically everyone who wants to contribute can do the mentioned steps at any time.
 
-### Pull Request Priorization
-For all pull requests, where all neccessary information is available and cla is signed thus triage is accepted, we need to do some priorization: 
+### Pull Request Prioritization
+For all pull requests, where all necessary information is available and cla is signed thus triage is accepted, we need to do some prioritization: 
 
 * Go through all pull requests with label [`triage-accepted`](https://github.com/kubernetes/ingress-nginx/pulls?q=is%3Aopen+is%3Apr+label%3Atriage%2Faccepted).  
 * Sync the `kind/*` and `priority/*` label from the linked issue for the pull request. If the pull request does not have any issue associated (which normally should not be the case), add an appropriate priority and kind label (one of: `priority/backlog`, `priority/critical-urgent`, `priority/important-longterm`, `priority/important-soon`) 
@@ -79,10 +79,10 @@ Labels are helpful for issues or pull requests to indicate in which lifecycle st
   * [`area/lua`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+label%3Aarea%2Flua+is%3Aissue+): Indicates that the issue is related to lua. Add with `/area lua`.
   * [`area/docs`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+label%3Aarea%2Fdocs+is%3Aissue): Indicates that the issue is related to documentation. Add with `/area docs` .
 * Priority: 
-  * [`needs-priority`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+is%3Aissue+label%3Aneeds-priority): Indicates that the issue has no priorization yet. Automatically added.
+  * [`needs-priority`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+is%3Aissue+label%3Aneeds-priority): Indicates that the issue has no prioritization yet. Automatically added.
   * [`priority/critical-urgent`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+label%3Apriority%2Fcritical-urgent+is%3Aissue+): indicates that the issue has highest priority. Add with `/priority critical-urgent`.
   * [`priority/important-soon`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+label%3Apriority%2Fimportant-soon+is%3Aissue+): indicates that the issue should be worked on either currently soon, ideally in time for the next release. Add with `/priority important-soon`.
-  * [`priority/important-longterm`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+label%3Apriority%2Fimportant-longterm+is%3Aissue+): indicates that the issue is not important for now, but should be worked on in one of the upcomming releases. Add with `/priority important-longterm`.
+  * [`priority/important-longterm`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+label%3Apriority%2Fimportant-longterm+is%3Aissue+): indicates that the issue is not important for now, but should be worked on in one of the upcoming releases. Add with `/priority important-longterm`.
   * [`priority/backlog`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+label%3Apriority%2Fbacklog+is%3Aissue+): Indicates that the issue has the lowest priority. Add with `/priority backlog`.
 * Other: 
   * [`help wanted`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22): indicates that the issue needs help from a contributor. Add with `/help`.
diff --git a/build/build-plugin.sh b/build/build-plugin.sh
index 578957eb4..93b6efbdf 100755
--- a/build/build-plugin.sh
+++ b/build/build-plugin.sh
@@ -57,7 +57,7 @@ function build_for_arch(){
     -trimpath -ldflags="-buildid= -w -s" \
       -X ${PKG}/version.RELEASE=${TAG} \
       -X ${PKG}/version.COMMIT=${COMMIT_SHA} \
-      -X ${PKG}/version.REPO=${REPO_INFO}" \
+      -X ${PKG}/version.REPO=${REPO_INFO} \
     -o "${release}/kubectl-ingress_nginx${extension}" "${PKG}/cmd/plugin"
 
     cp LICENSE ${release}
diff --git a/build/dev-env.sh b/build/dev-env.sh
index b089de206..d98f0bfbd 100755
--- a/build/dev-env.sh
+++ b/build/dev-env.sh
@@ -61,7 +61,7 @@ echo "[dev-env] building image"
 make build image
 docker tag "${REGISTRY}/controller:${TAG}" "${DEV_IMAGE}"
 
-export K8S_VERSION=${K8S_VERSION:-v1.20.2@sha256:8f7ea6e7642c0da54f04a7ee10431549c0257315b3a634f6ef2fecaaedb19bab}
+export K8S_VERSION=${K8S_VERSION:-v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6}
 
 KIND_CLUSTER_NAME="ingress-nginx-dev"
 
diff --git a/cmd/nginx/main.go b/cmd/nginx/main.go
index 69d12d05e..fb87ad84a 100644
--- a/cmd/nginx/main.go
+++ b/cmd/nginx/main.go
@@ -138,7 +138,9 @@ func main() {
 			klog.Fatalf("Error creating prometheus collector:  %v", err)
 		}
 	}
-	mc.Start()
+	// Pass the ValidationWebhook status to determine if we need to start the collector
+	// for the admissionWebhook
+	mc.Start(conf.ValidationWebhook)
 
 	if conf.EnableProfiling {
 		go registerProfiler()
diff --git a/docs/examples/affinity/cookie/ingress-samesite.yaml b/docs/examples/affinity/cookie/ingress-samesite.yaml
index edfa1b653..d03a25e39 100644
--- a/docs/examples/affinity/cookie/ingress-samesite.yaml
+++ b/docs/examples/affinity/cookie/ingress-samesite.yaml
@@ -15,10 +15,13 @@ spec:
   - host: stickyingress-samesite-none.example.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port:
+              number: 80
 ---
 apiVersion: networking.k8s.io/v1
 kind: Ingress
@@ -35,7 +38,10 @@ spec:
   - host: stickyingress-samesite-strict.example.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port:
+              number: 80
\ No newline at end of file
diff --git a/docs/examples/affinity/cookie/ingress.yaml b/docs/examples/affinity/cookie/ingress.yaml
index eac973fde..d3053021e 100644
--- a/docs/examples/affinity/cookie/ingress.yaml
+++ b/docs/examples/affinity/cookie/ingress.yaml
@@ -13,7 +13,10 @@ spec:
   - host: stickyingress.example.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port: 
+              number: 80
diff --git a/docs/examples/auth/basic/README.md b/docs/examples/auth/basic/README.md
index a5fa579e0..d3d255afb 100644
--- a/docs/examples/auth/basic/README.md
+++ b/docs/examples/auth/basic/README.md
@@ -47,9 +47,12 @@ spec:
     http:
       paths:
       - path: /
+        pathType: Prefix
         backend:
-          serviceName: http-svc
-          servicePort: 80
+          service: 
+            name: http-svc
+            port: 
+              number: 80
 " | kubectl create -f -
 ```
 
diff --git a/docs/examples/auth/client-certs/ingress.yaml b/docs/examples/auth/client-certs/ingress.yaml
index 7172081b4..90ff87514 100644
--- a/docs/examples/auth/client-certs/ingress.yaml
+++ b/docs/examples/auth/client-certs/ingress.yaml
@@ -19,10 +19,13 @@ spec:
   - host: mydomain.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port:
+              number: 80
   tls:
   - hosts:
     - mydomain.com
diff --git a/docs/examples/auth/external-auth/README.md b/docs/examples/auth/external-auth/README.md
index 235cca9cb..62ae72e2c 100644
--- a/docs/examples/auth/external-auth/README.md
+++ b/docs/examples/auth/external-auth/README.md
@@ -30,10 +30,13 @@ spec:
   - host: external-auth-01.sample.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service: 
+            name: http-svc
+            port: 
+              number: 80
 status:
   loadBalancer:
     ingress:
diff --git a/docs/examples/auth/external-auth/ingress.yaml b/docs/examples/auth/external-auth/ingress.yaml
index 2a58ca2e3..df80cbb25 100644
--- a/docs/examples/auth/external-auth/ingress.yaml
+++ b/docs/examples/auth/external-auth/ingress.yaml
@@ -9,7 +9,10 @@ spec:
   - host: external-auth-01.sample.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
\ No newline at end of file
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port:
+              number: 80
\ No newline at end of file
diff --git a/docs/examples/auth/oauth-external-auth/dashboard-ingress.yaml b/docs/examples/auth/oauth-external-auth/dashboard-ingress.yaml
index 725bf1dc5..4c0a2d373 100644
--- a/docs/examples/auth/oauth-external-auth/dashboard-ingress.yaml
+++ b/docs/examples/auth/oauth-external-auth/dashboard-ingress.yaml
@@ -11,11 +11,13 @@ spec:
   - host: __INGRESS_HOST__
     http:
       paths:
-      - backend:
-          serviceName: kubernetes-dashboard
-          servicePort: 80
-        path: /
-
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: kubernetes-dashboard
+            port:
+              number: 80
 ---
 
 apiVersion: networking.k8s.io/v1
@@ -28,10 +30,13 @@ spec:
   - host: __INGRESS_HOST__
     http:
       paths:
-      - backend:
-          serviceName: oauth2-proxy
-          servicePort: 4180
-        path: /oauth2
+      - path: /oauth2
+        pathType: Prefix
+        backend:
+          service:
+            name: oauth2-proxy
+            port:
+              number: 4180
   tls:
   - hosts:
     - __INGRESS_HOST__
diff --git a/docs/examples/chashsubset/deployment.yaml b/docs/examples/chashsubset/deployment.yaml
index 24a41dcc2..867cd01c0 100644
--- a/docs/examples/chashsubset/deployment.yaml
+++ b/docs/examples/chashsubset/deployment.yaml
@@ -64,7 +64,15 @@ metadata:
   name: nginxhello-ingress
   namespace: default
 spec:
-  backend:
-    serviceName: nginxhello
-    servicePort: 80
+  rules:
+  - host: foo.bar.com
+    http:
+      paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+              name: nginxhello
+              port:
+                number: 80
 
diff --git a/docs/examples/customization/configuration-snippets/ingress.yaml b/docs/examples/customization/configuration-snippets/ingress.yaml
index 70d9042c7..aaf31f0bb 100644
--- a/docs/examples/customization/configuration-snippets/ingress.yaml
+++ b/docs/examples/customization/configuration-snippets/ingress.yaml
@@ -10,7 +10,10 @@ spec:
   - host: custom.configuration.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port:
+              number: 80
diff --git a/docs/examples/customization/external-auth-headers/deploy/echo-service.yaml b/docs/examples/customization/external-auth-headers/deploy/echo-service.yaml
index 075421807..3a80ae834 100644
--- a/docs/examples/customization/external-auth-headers/deploy/echo-service.yaml
+++ b/docs/examples/customization/external-auth-headers/deploy/echo-service.yaml
@@ -56,10 +56,13 @@ spec:
   - host: public-demo-echo-service.kube.local
     http:
       paths:
-      - backend:
-          serviceName: demo-echo-service
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: demo-echo-service
+            port:
+              number: 80
 ---
 apiVersion: networking.k8s.io/v1
 kind: Ingress
@@ -74,7 +77,10 @@ spec:
   - host: secure-demo-echo-service.kube.local
     http:
       paths:
-      - backend:
-          serviceName: demo-echo-service
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: demo-echo-service
+            port:
+              number: 80
diff --git a/docs/examples/docker-registry/ingress-with-tls.yaml b/docs/examples/docker-registry/ingress-with-tls.yaml
index 11ccf6627..bef35c50b 100644
--- a/docs/examples/docker-registry/ingress-with-tls.yaml
+++ b/docs/examples/docker-registry/ingress-with-tls.yaml
@@ -17,7 +17,10 @@ spec:
   - host: registry.<your domain>
     http:
       paths:
-      - backend:
-          serviceName: docker-registry
-          servicePort: 5000
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: docker-registry
+            port:
+              number: 5000
\ No newline at end of file
diff --git a/docs/examples/docker-registry/ingress-without-tls.yaml b/docs/examples/docker-registry/ingress-without-tls.yaml
index 2d713cb8c..f64cbb295 100644
--- a/docs/examples/docker-registry/ingress-without-tls.yaml
+++ b/docs/examples/docker-registry/ingress-without-tls.yaml
@@ -12,7 +12,10 @@ spec:
   - host: registry.<your domain>
     http:
       paths:
-      - backend:
-          serviceName: docker-registry
-          servicePort: 5000
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: docker-registry
+            port:
+              number: 5000
diff --git a/docs/examples/multi-tls/multi-tls.yaml b/docs/examples/multi-tls/multi-tls.yaml
index b3985439e..aa2cf54d6 100644
--- a/docs/examples/multi-tls/multi-tls.yaml
+++ b/docs/examples/multi-tls/multi-tls.yaml
@@ -115,14 +115,20 @@ spec:
   - host: foo.bar.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port:
+              number: 80
   - host: bar.baz.com
     http:
       paths:
-      - backend:
-          serviceName: nginx
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service
+            name: nginx
+            port:
+              number: 80
diff --git a/docs/examples/rewrite/README.md b/docs/examples/rewrite/README.md
index 6ae71a5d8..3c48b100f 100644
--- a/docs/examples/rewrite/README.md
+++ b/docs/examples/rewrite/README.md
@@ -46,10 +46,13 @@ spec:
   - host: rewrite.bar.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /something(/|$)(.*)
+      - path: /something(/|$)(.*)
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port: 
+              number: 80
 ' | kubectl create -f -
 ```
 
@@ -78,10 +81,13 @@ spec:
   - host: approot.bar.com
     http:
       paths:
-      - backend:
-          serviceName: http-svc
-          servicePort: 80
-        path: /
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: http-svc
+            port: 
+              number: 80
 " | kubectl create -f -
 ```
 
diff --git a/docs/examples/static-ip/nginx-ingress.yaml b/docs/examples/static-ip/nginx-ingress.yaml
index 358942f5c..740f46e89 100644
--- a/docs/examples/static-ip/nginx-ingress.yaml
+++ b/docs/examples/static-ip/nginx-ingress.yaml
@@ -9,7 +9,11 @@ spec:
   rules:
   - http:
       paths:
-      - backend:
+      - path: /
+        pathType: Prefix
+        backend:
           # This assumes http-svc exists and routes to healthy endpoints.
-          serviceName: http-svc
-          servicePort: 80
+          service:
+            name: http-svc
+            port:
+              number: 80
diff --git a/docs/examples/tls-termination/README.md b/docs/examples/tls-termination/README.md
index f3096db2e..2df767e26 100644
--- a/docs/examples/tls-termination/README.md
+++ b/docs/examples/tls-termination/README.md
@@ -27,10 +27,13 @@ spec:
       http:
         paths:
         - path: /
+          pathType: Prefix
           backend:
             # This assumes http-svc exists and routes to healthy endpoints
-            serviceName: http-svc
-            servicePort: 80
+            service:
+              name: http-svc
+              port: 
+                number: 80
 ```
 
 The following command instructs the controller to terminate traffic using the provided
diff --git a/docs/examples/tls-termination/ingress.yaml b/docs/examples/tls-termination/ingress.yaml
index 2e989d1b0..debaa45b2 100644
--- a/docs/examples/tls-termination/ingress.yaml
+++ b/docs/examples/tls-termination/ingress.yaml
@@ -14,7 +14,10 @@ spec:
       http:
         paths:
         - path: /
+          pathType: Prefix
           backend:
             # This assumes http-svc exists and routes to healthy endpoints
-            serviceName: http-svc
-            servicePort: 80
+            service:
+              name: http-svc
+              port:
+                number: 80
diff --git a/docs/user-guide/fcgi-services.md b/docs/user-guide/fcgi-services.md
index 62271c2ce..03afc89d1 100644
--- a/docs/user-guide/fcgi-services.md
+++ b/docs/user-guide/fcgi-services.md
@@ -74,9 +74,13 @@ spec:
   - host: app.example.com
     http:
       paths:
-      - backend:
-          serviceName: example-service
-          servicePort: fastcgi
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: example-service
+            port:
+              name: fastcgi
 ```
 
 ## FastCGI Ingress Annotations
diff --git a/docs/user-guide/ingress-path-matching.md b/docs/user-guide/ingress-path-matching.md
index 6812e2cb5..e5e0d9a50 100644
--- a/docs/user-guide/ingress-path-matching.md
+++ b/docs/user-guide/ingress-path-matching.md
@@ -27,9 +27,12 @@ spec:
     http:
       paths:
       - path: /foo/.*
+        pathType: Prefix
         backend:
-          serviceName: test
-          servicePort: 80
+          service:
+            name: test
+            port:
+              number: 80
 ```
 
 The preceding ingress definition would translate to the following location block within the NGINX configuration for the `test.com` server:
@@ -61,13 +64,19 @@ spec:
     http:
       paths:
       - path: /foo/bar
+        pathType: Prefix
         backend:
-          serviceName: service1
-          servicePort: 80
+          service:
+            name: service1
+            port:
+              number: 80
       - path: /foo/bar/
+        pathType: Prefix
         backend:
-          serviceName: service2
-          servicePort: 80
+          service:
+            name: service2
+            port:
+              number: 80
 ```
 
 ```yaml
@@ -83,9 +92,12 @@ spec:
     http:
       paths:
       - path: /foo/bar/(.+)
+        pathType: Prefix
         backend:
-          serviceName: service3
-          servicePort: 80
+          service:
+            name: service3
+            port: 
+              number: 80
 ```
 
 The ingress controller would define the following location blocks, in order of descending length, within the NGINX template for the `test.com` server:
@@ -137,13 +149,19 @@ spec:
     http:
       paths:
       - path: /foo/bar/bar
+        pathType: Prefix
         backend:
-          serviceName: test
-          servicePort: 80
+          service:
+            name: test
+            port: 
+              number: 80
       - path: /foo/bar/[A-Z0-9]{3}
+        pathType: Prefix
         backend:
-          serviceName: test
-          servicePort: 80
+          service:
+            name: test
+            port: 
+              number: 80
 ```
 
 The ingress controller would define the following location blocks (in this order) within the NGINX template for the `test.com` server:
diff --git a/docs/user-guide/nginx-configuration/annotations.md b/docs/user-guide/nginx-configuration/annotations.md
index ca515f9e2..19bd3947f 100755
--- a/docs/user-guide/nginx-configuration/annotations.md
+++ b/docs/user-guide/nginx-configuration/annotations.md
@@ -354,10 +354,13 @@ CORS can be controlled with the following annotations:
 
 * `nginx.ingress.kubernetes.io/cors-allow-origin`: Controls what's the accepted Origin for CORS.
 
-    This is a single field value, with the following format: `http(s)://origin-site.com` or `http(s)://origin-site.com:port`
+    This is a multi-valued field, separated by ','. It must follow this format: `http(s)://origin-site.com` or `http(s)://origin-site.com:port`
 
     - Default: `*`
-    - Example: `nginx.ingress.kubernetes.io/cors-allow-origin: "https://origin-site.com:4443"`
+    - Example: `nginx.ingress.kubernetes.io/cors-allow-origin: "https://origin-site.com:4443, http://origin-site.com, https://example.org:1199"`
+
+    It also supports single level wildcard subdomains and follows this format: `http(s)://*.foo.bar`, `http(s)://*.bar.foo:8080` or `http(s)://*.abc.bar.foo:9000`
+    - Example: `nginx.ingress.kubernetes.io/cors-allow-origin: "https://*.origin-site.com:4443, http://*.origin-site.com, https://example.org:1199"`
 
 * `nginx.ingress.kubernetes.io/cors-allow-credentials`: Controls if credentials can be passed during CORS operations.
 
diff --git a/docs/user-guide/nginx-configuration/configmap.md b/docs/user-guide/nginx-configuration/configmap.md
index 4fdda21ba..21cb2799e 100755
--- a/docs/user-guide/nginx-configuration/configmap.md
+++ b/docs/user-guide/nginx-configuration/configmap.md
@@ -98,6 +98,7 @@ The following table shows a configuration option's name, type, and the default v
 |[use-geoip2](#use-geoip2)|bool|"false"|
 |[enable-brotli](#enable-brotli)|bool|"false"|
 |[brotli-level](#brotli-level)|int|4|
+|[brotli-min-length](#brotli-min-length)|int|20|
 |[brotli-types](#brotli-types)|string|"application/xml+rss application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component"|
 |[use-http2](#use-http2)|bool|"true"|
 |[gzip-level](#gzip-level)|int|1|
@@ -665,6 +666,10 @@ The default mime type list to compress is: `application/xml+rss application/atom
 
 Sets the Brotli Compression Level that will be used. _**default:**_ 4
 
+## brotli-min-length
+
+Minimum length of responses, in bytes, that will be eligible for brotli compression. _**default:**_ 20
+
 ## brotli-types
 
 Sets the MIME Types that will be compressed on-the-fly by brotli.
diff --git a/docs/user-guide/third-party-addons/opentracing.md b/docs/user-guide/third-party-addons/opentracing.md
index 6c2918046..e27f32f6b 100644
--- a/docs/user-guide/third-party-addons/opentracing.md
+++ b/docs/user-guide/third-party-addons/opentracing.md
@@ -189,10 +189,13 @@ In the Zipkin interface we can see the details:
         - host: example.com
           http:
             paths:
-            - backend:
-                serviceName: echoheaders-x
-                servicePort: 80
-              path: /echo
+            - path: /echo
+              pathType: Prefix
+              backend:
+                service:
+                  name: echoheaders-x
+                  port:
+                    number: 80
       ' | kubectl apply -f -
     ```
 
diff --git a/go.mod b/go.mod
index 2f64cfd89..49e380774 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/mitchellh/hashstructure v1.1.0
 	github.com/mitchellh/mapstructure v1.4.2
 	github.com/moul/pb v0.0.0-20180404114147-54bdd96e6a52
-	github.com/ncabatoff/process-exporter v0.7.5
+	github.com/ncabatoff/process-exporter v0.7.9
 	github.com/onsi/ginkgo v1.16.4
 	github.com/opencontainers/runc v1.0.2
 	github.com/pkg/errors v0.9.1
@@ -78,13 +78,14 @@ require (
 	github.com/go-openapi/jsonreference v0.19.3 // indirect
 	github.com/go-openapi/spec v0.19.5 // indirect
 	github.com/go-openapi/swag v0.19.5 // indirect
+	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
 	github.com/godbus/dbus/v5 v5.0.4 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/gomarkdown/markdown v0.0.0-20200824053859-8c8b3816f167 // indirect
 	github.com/google/btree v1.0.0 // indirect
-	github.com/google/go-cmp v0.5.5 // indirect
+	github.com/google/go-cmp v0.5.6 // indirect
 	github.com/google/go-querystring v1.0.0 // indirect
 	github.com/google/gofuzz v1.1.0 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
@@ -108,7 +109,7 @@ require (
 	github.com/nxadm/tail v1.4.8 // indirect
 	github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
 	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
-	github.com/prometheus/procfs v0.6.0 // indirect
+	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/sergi/go-diff v1.1.0 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 718fba62f..21a2bb5c4 100644
--- a/go.sum
+++ b/go.sum
@@ -269,6 +269,7 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K
 github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
 github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
@@ -329,8 +330,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -445,6 +447,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -528,9 +532,8 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS
 github.com/ncabatoff/fakescraper v0.0.0-20201102132415-4b37ba603d65/go.mod h1:Tx6UMSMyIsjLG/VU/F6xA1+0XI+/f9o1dGJnf1l+bPg=
 github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833 h1:t4WWQ9I797y7QUgeEjeXnVb+oYuEDQc6gLvrZJTYo94=
 github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833/go.mod h1:0CznHmXSjMEqs5Tezj/w2emQoM41wzYM9KpDKUHPYag=
-github.com/ncabatoff/process-exporter v0.7.5 h1:KPB7abQEny4kaSge/WFRQb04/UbLmUgHxrpWAU+voRE=
-github.com/ncabatoff/process-exporter v0.7.5/go.mod h1:RMjrx3Qn8l2pgCD27g45xbko4UDpVVuHC8Cd2YXPtWA=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/ncabatoff/process-exporter v0.7.9 h1:RRvLF8pbWwLryEZiNMKJGfCTQkJW8SNabneiKQq3DBY=
+github.com/ncabatoff/process-exporter v0.7.9/go.mod h1:I/1yUBpScNw5UC/Qf2DC9vWqol2cR6jPqSVulMEnzxQ=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
@@ -578,6 +581,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -594,7 +598,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
 github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -610,7 +613,6 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
 github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
 github.com/prometheus/common v0.31.1 h1:d18hG4PkHnNAKNMOmFuXFaiY8Us0nird/2m60uS1AMs=
 github.com/prometheus/common v0.31.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
@@ -621,13 +623,17 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
 github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
@@ -956,7 +962,6 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1190,8 +1195,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
-gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
diff --git a/internal/ingress/annotations/cors/main.go b/internal/ingress/annotations/cors/main.go
index 44947a014..d2f232af1 100644
--- a/internal/ingress/annotations/cors/main.go
+++ b/internal/ingress/annotations/cors/main.go
@@ -18,8 +18,10 @@ package cors
 
 import (
 	"regexp"
+	"strings"
 
 	networking "k8s.io/api/networking/v1"
+	"k8s.io/klog/v2"
 
 	"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
 	"k8s.io/ingress-nginx/internal/ingress/resolver"
@@ -36,7 +38,7 @@ var (
 	// Regex are defined here to prevent information leak, if user tries to set anything not valid
 	// that could cause the Response to contain some internal value/variable (like returning $pid, $upstream_addr, etc)
 	// Origin must contain a http/s Origin (including or not the port) or the value '*'
-	corsOriginRegex = regexp.MustCompile(`^(https?://[A-Za-z0-9\-\.]*(:[0-9]+)?|\*)?$`)
+	corsOriginRegex = regexp.MustCompile(`^(https?://(\*\.)?[A-Za-z0-9\-\.]*(:[0-9]+)?|\*)?$`)
 	// Method must contain valid methods list (PUT, GET, POST, BLA)
 	// May contain or not spaces between each verb
 	corsMethodsRegex = regexp.MustCompile(`^([A-Za-z]+,?\s?)+$`)
@@ -54,13 +56,13 @@ type cors struct {
 
 // Config contains the Cors configuration to be used in the Ingress
 type Config struct {
-	CorsEnabled          bool   `json:"corsEnabled"`
-	CorsAllowOrigin      string `json:"corsAllowOrigin"`
-	CorsAllowMethods     string `json:"corsAllowMethods"`
-	CorsAllowHeaders     string `json:"corsAllowHeaders"`
-	CorsAllowCredentials bool   `json:"corsAllowCredentials"`
-	CorsExposeHeaders    string `json:"corsExposeHeaders"`
-	CorsMaxAge           int    `json:"corsMaxAge"`
+	CorsEnabled          bool     `json:"corsEnabled"`
+	CorsAllowOrigin      []string `json:"corsAllowOrigin"`
+	CorsAllowMethods     string   `json:"corsAllowMethods"`
+	CorsAllowHeaders     string   `json:"corsAllowHeaders"`
+	CorsAllowCredentials bool     `json:"corsAllowCredentials"`
+	CorsExposeHeaders    string   `json:"corsExposeHeaders"`
+	CorsMaxAge           int      `json:"corsMaxAge"`
 }
 
 // NewParser creates a new CORS annotation parser
@@ -91,13 +93,20 @@ func (c1 *Config) Equal(c2 *Config) bool {
 	if c1.CorsAllowMethods != c2.CorsAllowMethods {
 		return false
 	}
-	if c1.CorsAllowOrigin != c2.CorsAllowOrigin {
+	if c1.CorsEnabled != c2.CorsEnabled {
 		return false
 	}
-	if c1.CorsEnabled != c2.CorsEnabled {
+
+	if len(c1.CorsAllowOrigin) != len(c2.CorsAllowOrigin) {
 		return false
 	}
 
+	for i, v := range c1.CorsAllowOrigin {
+		if v != c2.CorsAllowOrigin[i] {
+			return false
+		}
+	}
+
 	return true
 }
 
@@ -112,9 +121,23 @@ func (c cors) Parse(ing *networking.Ingress) (interface{}, error) {
 		config.CorsEnabled = false
 	}
 
-	config.CorsAllowOrigin, err = parser.GetStringAnnotation("cors-allow-origin", ing)
-	if err != nil || !corsOriginRegex.MatchString(config.CorsAllowOrigin) {
-		config.CorsAllowOrigin = "*"
+	unparsedOrigins, err := parser.GetStringAnnotation("cors-allow-origin", ing)
+	if err == nil {
+		config.CorsAllowOrigin = strings.Split(unparsedOrigins, ",")
+		for i, origin := range config.CorsAllowOrigin {
+			origin = strings.TrimSpace(origin)
+			if origin == "*" {
+				config.CorsAllowOrigin = []string{"*"}
+				break
+			}
+			if !corsOriginRegex.MatchString(origin) {
+				klog.Errorf("Error parsing cors-allow-origin parameters. Supplied incorrect origin: %s. Skipping.", origin)
+				config.CorsAllowOrigin = append(config.CorsAllowOrigin[:i], config.CorsAllowOrigin[i+1:]...)
+			}
+			klog.Infof("Current config.corsAllowOrigin %v", config.CorsAllowOrigin)
+		}
+	} else {
+		config.CorsAllowOrigin = []string{"*"}
 	}
 
 	config.CorsAllowHeaders, err = parser.GetStringAnnotation("cors-allow-headers", ing)
@@ -143,5 +166,4 @@ func (c cors) Parse(ing *networking.Ingress) (interface{}, error) {
 	}
 
 	return config, nil
-
 }
diff --git a/internal/ingress/controller/config/config.go b/internal/ingress/controller/config/config.go
index b1a5fc8c4..2d853cfb0 100644
--- a/internal/ingress/controller/config/config.go
+++ b/internal/ingress/controller/config/config.go
@@ -413,6 +413,9 @@ type Configuration struct {
 	// Brotli Compression Level that will be used
 	BrotliLevel int `json:"brotli-level,omitempty"`
 
+	// Minimum length of responses, in bytes, that will be eligible for brotli compression
+	BrotliMinLength int `json:"brotli-min-length,omitempty"`
+
 	// MIME Types that will be compressed on-the-fly using Brotli module
 	BrotliTypes string `json:"brotli-types,omitempty"`
 
@@ -778,6 +781,7 @@ func NewDefault() Configuration {
 		BlockUserAgents:                  defBlockEntity,
 		BlockReferers:                    defBlockEntity,
 		BrotliLevel:                      4,
+		BrotliMinLength:                  20,
 		BrotliTypes:                      brotliTypes,
 		ClientHeaderBufferSize:           "1k",
 		ClientHeaderTimeout:              60,
diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go
index f935f5fff..d648bb3ae 100644
--- a/internal/ingress/controller/controller.go
+++ b/internal/ingress/controller/controller.go
@@ -215,6 +215,8 @@ func (n *NGINXController) syncIngress(interface{}) error {
 // CheckIngress returns an error in case the provided ingress, when added
 // to the current configuration, generates an invalid configuration
 func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
+	startCheck := time.Now().UnixNano() / 1000000
+
 	if ing == nil {
 		// no ingress to add, no state change
 		return nil
@@ -233,7 +235,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
 	if n.cfg.DisableCatchAll && ing.Spec.DefaultBackend != nil {
 		return fmt.Errorf("This deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.backend' or set DisableCatchAll flag to false.")
 	}
-
+	startRender := time.Now().UnixNano() / 1000000
 	cfg := n.store.GetBackendConfiguration()
 	cfg.Resolver = n.resolver
 
@@ -267,7 +269,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
 		Ingress:           *ing,
 		ParsedAnnotations: annotations.NewAnnotationExtractor(n.store).Extract(ing),
 	})
-
+	startTest := time.Now().UnixNano() / 1000000
 	_, servers, pcfg := n.getConfiguration(ings)
 
 	err := checkOverlap(ing, allIngresses, servers)
@@ -275,9 +277,10 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
 		n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
 		return err
 	}
-
+	testedSize := len(ings)
 	if n.cfg.DisableFullValidationTest {
 		_, _, pcfg = n.getConfiguration(ings[len(ings)-1:])
+		testedSize = 1
 	}
 
 	content, err := n.generateTemplate(cfg, *pcfg)
@@ -291,8 +294,16 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
 		n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
 		return err
 	}
-
 	n.metricCollector.IncCheckCount(ing.ObjectMeta.Namespace, ing.Name)
+	endCheck := time.Now().UnixNano() / 1000000
+	n.metricCollector.SetAdmissionMetrics(
+		float64(testedSize),
+		float64(endCheck-startTest)/1000,
+		float64(len(ings)),
+		float64(startTest-startRender)/1000,
+		float64(len(content)),
+		float64(endCheck-startCheck)/1000,
+	)
 	return nil
 }
 
diff --git a/internal/ingress/controller/nginx.go b/internal/ingress/controller/nginx.go
index ddde11bc8..b77d1d870 100644
--- a/internal/ingress/controller/nginx.go
+++ b/internal/ingress/controller/nginx.go
@@ -241,7 +241,8 @@ type NGINXController struct {
 
 	store store.Storer
 
-	metricCollector metric.Collector
+	metricCollector    metric.Collector
+	admissionCollector metric.Collector
 
 	validationWebhookServer *http.Server
 
diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go
index dc4b78c1a..1147c8e5d 100644
--- a/internal/ingress/controller/template/template.go
+++ b/internal/ingress/controller/template/template.go
@@ -276,6 +276,7 @@ var (
 		"shouldLoadAuthDigestModule":         shouldLoadAuthDigestModule,
 		"shouldLoadInfluxDBModule":           shouldLoadInfluxDBModule,
 		"buildServerName":                    buildServerName,
+		"buildCorsOriginRegex":               buildCorsOriginRegex,
 	}
 )
 
@@ -1676,3 +1677,29 @@ func convertGoSliceIntoLuaTable(goSliceInterface interface{}, emptyStringAsNil b
 		return "", fmt.Errorf("could not process type: %s", kind)
 	}
 }
+
+func buildOriginRegex(origin string) string {
+	origin = regexp.QuoteMeta(origin)
+	origin = strings.Replace(origin, "\\*", "[A-Za-z0-9]+", 1)
+	return fmt.Sprintf("(%s)", origin)
+}
+
+func buildCorsOriginRegex(corsOrigins []string) string {
+	if len(corsOrigins) == 1 && corsOrigins[0] == "*" {
+		return "set $http_origin *;\nset $cors 'true';"
+	}
+
+	var originsRegex string = "if ($http_origin ~* ("
+	for i, origin := range corsOrigins {
+		originTrimmed := strings.TrimSpace(origin)
+		if len(originTrimmed) > 0 {
+			builtOrigin := buildOriginRegex(originTrimmed)
+			originsRegex += builtOrigin
+			if i != len(corsOrigins)-1 {
+				originsRegex = originsRegex + "|"
+			}
+		}
+	}
+	originsRegex = originsRegex + ")$ ) { set $cors 'true'; }"
+	return originsRegex
+}
diff --git a/internal/ingress/metric/collectors/admission.go b/internal/ingress/metric/collectors/admission.go
new file mode 100644
index 000000000..cf42fbaa1
--- /dev/null
+++ b/internal/ingress/metric/collectors/admission.go
@@ -0,0 +1,157 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package collectors
+
+import (
+	"fmt"
+
+	"github.com/prometheus/client_golang/prometheus"
+	"k8s.io/klog/v2"
+)
+
+// AdmissionCollector stores prometheus metrics of the admission webhook
+type AdmissionCollector struct {
+	prometheus.Collector
+
+	testedIngressLength prometheus.Gauge
+	testedIngressTime   prometheus.Gauge
+
+	renderingIngressLength prometheus.Gauge
+	renderingIngressTime   prometheus.Gauge
+
+	admissionTime prometheus.Gauge
+
+	testedConfigurationSize prometheus.Gauge
+
+	constLabels prometheus.Labels
+	labels      prometheus.Labels
+}
+
+// NewAdmissionCollector creates a new AdmissionCollector instance for the admission collector
+func NewAdmissionCollector(pod, namespace, class string) *AdmissionCollector {
+	constLabels := prometheus.Labels{
+		"controller_namespace": namespace,
+		"controller_class":     class,
+		"controller_pod":       pod,
+	}
+
+	am := &AdmissionCollector{
+		constLabels: constLabels,
+
+		labels: prometheus.Labels{
+			"namespace": namespace,
+			"class":     class,
+		},
+
+		testedIngressLength: prometheus.NewGauge(
+			prometheus.GaugeOpts{
+				Name:        "admission_tested_ingresses",
+				Help:        "The length of ingresses processed by the admission controller",
+				Namespace:   PrometheusNamespace,
+				ConstLabels: constLabels,
+			}),
+		testedIngressTime: prometheus.NewGauge(
+			prometheus.GaugeOpts{
+				Name:        "admission_tested_duration",
+				Help:        "The processing duration of the admission controller tests (float seconds)",
+				Namespace:   PrometheusNamespace,
+				ConstLabels: constLabels,
+			}),
+		renderingIngressLength: prometheus.NewGauge(
+			prometheus.GaugeOpts{
+				Name:        "admission_render_ingresses",
+				Help:        "The length of ingresses rendered by the admission controller",
+				Namespace:   PrometheusNamespace,
+				ConstLabels: constLabels,
+			}),
+		renderingIngressTime: prometheus.NewGauge(
+			prometheus.GaugeOpts{
+				Name:        "admission_render_duration",
+				Help:        "The processing duration of ingresses rendering by the admission controller (float seconds)",
+				Namespace:   PrometheusNamespace,
+				ConstLabels: constLabels,
+			}),
+		testedConfigurationSize: prometheus.NewGauge(
+			prometheus.GaugeOpts{
+				Namespace:   PrometheusNamespace,
+				Name:        "admission_config_size",
+				Help:        "The size of the tested configuration",
+				ConstLabels: constLabels,
+			}),
+		admissionTime: prometheus.NewGauge(
+			prometheus.GaugeOpts{
+				Name:        "admission_roundtrip_duration",
+				Help:        "The complete duration of the admission controller at the time to process a new event (float seconds)",
+				Namespace:   PrometheusNamespace,
+				ConstLabels: constLabels,
+			}),
+	}
+	return am
+}
+
+// Describe implements prometheus.Collector
+func (am AdmissionCollector) Describe(ch chan<- *prometheus.Desc) {
+	am.testedIngressLength.Describe(ch)
+	am.testedIngressTime.Describe(ch)
+	am.renderingIngressLength.Describe(ch)
+	am.renderingIngressTime.Describe(ch)
+	am.testedConfigurationSize.Describe(ch)
+	am.admissionTime.Describe(ch)
+}
+
+// Collect implements the prometheus.Collector interface.
+func (am AdmissionCollector) Collect(ch chan<- prometheus.Metric) {
+	am.testedIngressLength.Collect(ch)
+	am.testedIngressTime.Collect(ch)
+	am.renderingIngressLength.Collect(ch)
+	am.renderingIngressTime.Collect(ch)
+	am.testedConfigurationSize.Collect(ch)
+	am.admissionTime.Collect(ch)
+}
+
+// ByteFormat formats humanReadable bytes
+func ByteFormat(bytes int64) string {
+	const unit = 1000
+	if bytes < unit {
+		return fmt.Sprintf("%d B", bytes)
+	}
+	div, exp := int64(unit), 0
+	for n := bytes / unit; n >= unit; n /= unit {
+		div *= unit
+		exp++
+	}
+	return fmt.Sprintf("%.1f%cB",
+		float64(bytes)/float64(div), "kMGTPE"[exp])
+}
+
+// SetAdmissionMetrics sets the values for AdmissionMetrics that can be called externally
+func (am *AdmissionCollector) SetAdmissionMetrics(testedIngressLength float64, testedIngressTime float64, renderingIngressLength float64, renderingIngressTime float64, testedConfigurationSize float64, admissionTime float64) {
+	am.testedIngressLength.Set(testedIngressLength)
+	am.testedIngressTime.Set(testedIngressTime)
+	am.renderingIngressLength.Set(renderingIngressLength)
+	am.renderingIngressTime.Set(renderingIngressTime)
+	am.testedConfigurationSize.Set(testedConfigurationSize)
+	am.admissionTime.Set(admissionTime)
+	klog.Infof("processed ingress via admission controller {testedIngressLength:%v testedIngressTime:%vs renderingIngressLength:%v renderingIngressTime:%vs admissionTime:%vs testedConfigurationSize:%v}",
+		testedIngressLength,
+		testedIngressTime,
+		renderingIngressLength,
+		renderingIngressTime,
+		ByteFormat(int64(testedConfigurationSize)),
+		admissionTime,
+	)
+}
diff --git a/internal/ingress/metric/dummy.go b/internal/ingress/metric/dummy.go
index 59a9144e0..922a21604 100644
--- a/internal/ingress/metric/dummy.go
+++ b/internal/ingress/metric/dummy.go
@@ -32,6 +32,9 @@ type DummyCollector struct{}
 // ConfigSuccess ...
 func (dc DummyCollector) ConfigSuccess(uint64, bool) {}
 
+// SetAdmissionMetrics ...
+func (dc DummyCollector) SetAdmissionMetrics(float64, float64, float64, float64, float64, float64) {}
+
 // IncReloadCount ...
 func (dc DummyCollector) IncReloadCount() {}
 
@@ -48,10 +51,10 @@ func (dc DummyCollector) IncCheckErrorCount(string, string) {}
 func (dc DummyCollector) RemoveMetrics(ingresses, endpoints []string) {}
 
 // Start ...
-func (dc DummyCollector) Start() {}
+func (dc DummyCollector) Start(admissionStatus string) {}
 
 // Stop ...
-func (dc DummyCollector) Stop() {}
+func (dc DummyCollector) Stop(admissionStatus string) {}
 
 // SetSSLExpireTime ...
 func (dc DummyCollector) SetSSLExpireTime([]*ingress.Server) {}
diff --git a/internal/ingress/metric/main.go b/internal/ingress/metric/main.go
index 64810dd36..0cc07fe28 100644
--- a/internal/ingress/metric/main.go
+++ b/internal/ingress/metric/main.go
@@ -36,6 +36,8 @@ type Collector interface {
 	IncReloadCount()
 	IncReloadErrorCount()
 
+	SetAdmissionMetrics(float64, float64, float64, float64, float64, float64)
+
 	OnStartedLeading(string)
 	OnStoppedLeading(string)
 
@@ -49,15 +51,16 @@ type Collector interface {
 	// SetHosts sets the hostnames that are being served by the ingress controller
 	SetHosts(sets.String)
 
-	Start()
-	Stop()
+	Start(string)
+	Stop(string)
 }
 
 type collector struct {
 	nginxStatus  collectors.NGINXStatusCollector
 	nginxProcess collectors.NGINXProcessCollector
 
-	ingressController *collectors.Controller
+	ingressController   *collectors.Controller
+	admissionController *collectors.AdmissionCollector
 
 	socket *collectors.SocketCollector
 
@@ -90,11 +93,14 @@ func NewCollector(metricsPerHost bool, registry *prometheus.Registry, ingresscla
 
 	ic := collectors.NewController(podName, podNamespace, ingressclass)
 
+	am := collectors.NewAdmissionCollector(podName, podNamespace, ingressclass)
+
 	return Collector(&collector{
 		nginxStatus:  nc,
 		nginxProcess: pc,
 
-		ingressController: ic,
+		admissionController: am,
+		ingressController:   ic,
 
 		socket: s,
 
@@ -127,9 +133,12 @@ func (c *collector) RemoveMetrics(ingresses, hosts []string) {
 	c.ingressController.RemoveMetrics(hosts, c.registry)
 }
 
-func (c *collector) Start() {
+func (c *collector) Start(admissionStatus string) {
 	c.registry.MustRegister(c.nginxStatus)
 	c.registry.MustRegister(c.nginxProcess)
+	if admissionStatus != "" {
+		c.registry.MustRegister(c.admissionController)
+	}
 	c.registry.MustRegister(c.ingressController)
 	c.registry.MustRegister(c.socket)
 
@@ -143,9 +152,12 @@ func (c *collector) Start() {
 	go c.socket.Start()
 }
 
-func (c *collector) Stop() {
+func (c *collector) Stop(admissionStatus string) {
 	c.registry.Unregister(c.nginxStatus)
 	c.registry.Unregister(c.nginxProcess)
+	if admissionStatus != "" {
+		c.registry.Unregister(c.admissionController)
+	}
 	c.registry.Unregister(c.ingressController)
 	c.registry.Unregister(c.socket)
 
@@ -167,6 +179,17 @@ func (c *collector) SetHosts(hosts sets.String) {
 	c.socket.SetHosts(hosts)
 }
 
+func (c *collector) SetAdmissionMetrics(testedIngressLength float64, testedIngressTime float64, renderingIngressLength float64, renderingIngressTime float64, testedConfigurationSize float64, admissionTime float64) {
+	c.admissionController.SetAdmissionMetrics(
+		testedIngressLength,
+		testedIngressTime,
+		renderingIngressLength,
+		renderingIngressTime,
+		testedConfigurationSize,
+		admissionTime,
+	)
+}
+
 // OnStartedLeading indicates the pod was elected as the leader
 func (c *collector) OnStartedLeading(electionID string) {
 	setLeader(true)
diff --git a/rootfs/etc/nginx/template/nginx.tmpl b/rootfs/etc/nginx/template/nginx.tmpl
index bfe0703ff..d579165b7 100755
--- a/rootfs/etc/nginx/template/nginx.tmpl
+++ b/rootfs/etc/nginx/template/nginx.tmpl
@@ -320,6 +320,7 @@ http {
     {{ if $cfg.EnableBrotli }}
     brotli on;
     brotli_comp_level {{ $cfg.BrotliLevel }};
+    brotli_min_length {{ $cfg.BrotliMinLength }};
     brotli_types {{ $cfg.BrotliTypes }};
     {{ end }}
 
@@ -866,22 +867,33 @@ stream {
 {{ define "CORS" }}
      {{ $cors := .CorsConfig }}
      # Cors Preflight methods needs additional options and different Return Code
+     {{ if $cors.CorsAllowOrigin }}
+        {{ buildCorsOriginRegex $cors.CorsAllowOrigin }}
+     {{ end }}
      if ($request_method = 'OPTIONS') {
-        more_set_headers 'Access-Control-Allow-Origin: {{ $cors.CorsAllowOrigin }}';
+        set $cors ${cors}options;
+     }
+
+     if ($cors = "true") {
+        more_set_headers 'Access-Control-Allow-Origin: $http_origin';
         {{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }}
         more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}';
         more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}';
         {{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }}
         more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}';
-        more_set_headers 'Content-Type: text/plain charset=UTF-8';
-        more_set_headers 'Content-Length: 0';
-        return 204;
      }
 
-        more_set_headers 'Access-Control-Allow-Origin: {{ $cors.CorsAllowOrigin }}';
+     if ($cors = "trueoptions") {
+        more_set_headers 'Access-Control-Allow-Origin: $http_origin';
         {{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }}
+        more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}';
+        more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}';
         {{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }}
-
+        more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}';
+        more_set_headers 'Content-Type: text/plain charset=UTF-8';
+        more_set_headers 'Content-Length: 0';
+        return 204;
+     }
 {{ end }}
 
 {{/* definition of server-template to avoid repetitions with server-alias */}}


Test patch:
diff --git a/internal/ingress/annotations/annotations_test.go b/internal/ingress/annotations/annotations_test.go
index ada81117d..8eec999cc 100644
--- a/internal/ingress/annotations/annotations_test.go
+++ b/internal/ingress/annotations/annotations_test.go
@@ -215,15 +215,15 @@ func TestCors(t *testing.T) {
 		corsenabled bool
 		methods     string
 		headers     string
-		origin      string
+		origin      []string
 		credentials bool
 		expose      string
 	}{
-		{map[string]string{annotationCorsEnabled: "true"}, true, defaultCorsMethods, defaultCorsHeaders, "*", true, ""},
-		{map[string]string{annotationCorsEnabled: "true", annotationCorsAllowMethods: "POST, GET, OPTIONS", annotationCorsAllowHeaders: "$nginx_version", annotationCorsAllowCredentials: "false", annotationCorsExposeHeaders: "X-CustomResponseHeader"}, true, "POST, GET, OPTIONS", defaultCorsHeaders, "*", false, "X-CustomResponseHeader"},
-		{map[string]string{annotationCorsEnabled: "true", annotationCorsAllowCredentials: "false"}, true, defaultCorsMethods, defaultCorsHeaders, "*", false, ""},
-		{map[string]string{}, false, defaultCorsMethods, defaultCorsHeaders, "*", true, ""},
-		{nil, false, defaultCorsMethods, defaultCorsHeaders, "*", true, ""},
+		{map[string]string{annotationCorsEnabled: "true"}, true, defaultCorsMethods, defaultCorsHeaders, []string{"*"}, true, ""},
+		{map[string]string{annotationCorsEnabled: "true", annotationCorsAllowMethods: "POST, GET, OPTIONS", annotationCorsAllowHeaders: "$nginx_version", annotationCorsAllowCredentials: "false", annotationCorsExposeHeaders: "X-CustomResponseHeader"}, true, "POST, GET, OPTIONS", defaultCorsHeaders, []string{"*"}, false, "X-CustomResponseHeader"},
+		{map[string]string{annotationCorsEnabled: "true", annotationCorsAllowCredentials: "false"}, true, defaultCorsMethods, defaultCorsHeaders, []string{"*"}, false, ""},
+		{map[string]string{}, false, defaultCorsMethods, defaultCorsHeaders, []string{"*"}, true, ""},
+		{nil, false, defaultCorsMethods, defaultCorsHeaders, []string{"*"}, true, ""},
 	}
 
 	for _, foo := range fooAnns {
@@ -243,12 +243,18 @@ func TestCors(t *testing.T) {
 			t.Errorf("Returned %v but expected %v for Cors Methods", r.CorsAllowMethods, foo.methods)
 		}
 
-		if r.CorsAllowOrigin != foo.origin {
-			t.Errorf("Returned %v but expected %v for Cors Methods", r.CorsAllowOrigin, foo.origin)
+		if len(r.CorsAllowOrigin) != len(foo.origin) {
+			t.Errorf("Lengths of Cors Origins are not equal. Expected %v - Actual %v", r.CorsAllowOrigin, foo.origin)
+		}
+
+		for i, v := range r.CorsAllowOrigin {
+			if v != foo.origin[i] {
+				t.Errorf("Values of Cors Origins are not equal. Expected %v - Actual %v", r.CorsAllowOrigin, foo.origin)
+			}
 		}
 
 		if r.CorsAllowCredentials != foo.credentials {
-			t.Errorf("Returned %v but expected %v for Cors Methods", r.CorsAllowCredentials, foo.credentials)
+			t.Errorf("Returned %v but expected %v for Cors Credentials", r.CorsAllowCredentials, foo.credentials)
 		}
 
 	}
diff --git a/internal/ingress/annotations/cors/main_test.go b/internal/ingress/annotations/cors/main_test.go
index a65bf12cd..086a59d89 100644
--- a/internal/ingress/annotations/cors/main_test.go
+++ b/internal/ingress/annotations/cors/main_test.go
@@ -110,7 +110,7 @@ func TestIngressCorsConfigValid(t *testing.T) {
 		t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-methods")], nginxCors.CorsAllowMethods)
 	}
 
-	if nginxCors.CorsAllowOrigin != "https://origin123.test.com:4443" {
+	if nginxCors.CorsAllowOrigin[0] != "https://origin123.test.com:4443" {
 		t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-origin")], nginxCors.CorsAllowOrigin)
 	}
 
@@ -164,10 +164,6 @@ func TestIngressCorsConfigInvalid(t *testing.T) {
 		t.Errorf("expected %v but returned %v", defaultCorsHeaders, nginxCors.CorsAllowMethods)
 	}
 
-	if nginxCors.CorsAllowOrigin != "*" {
-		t.Errorf("expected %v but returned %v", "*", nginxCors.CorsAllowOrigin)
-	}
-
 	if nginxCors.CorsExposeHeaders != "" {
 		t.Errorf("expected %v but returned %v", "", nginxCors.CorsExposeHeaders)
 	}
diff --git a/internal/ingress/metric/collectors/admission_test.go b/internal/ingress/metric/collectors/admission_test.go
new file mode 100644
index 000000000..68208ad3e
--- /dev/null
+++ b/internal/ingress/metric/collectors/admission_test.go
@@ -0,0 +1,122 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package collectors
+
+import (
+	"testing"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+func TestAdmissionCounters(t *testing.T) {
+	const (
+		metadataFirst = `
+			# HELP nginx_ingress_controller_admission_config_size The size of the tested configuration
+			# TYPE nginx_ingress_controller_admission_config_size gauge
+			# HELP nginx_ingress_controller_admission_roundtrip_duration The complete duration of the admission controller at the time to process a new event (float seconds)
+			# TYPE nginx_ingress_controller_admission_roundtrip_duration gauge
+		`
+		metadataSecond = `
+			# HELP nginx_ingress_controller_admission_render_ingresses The length of ingresses rendered by the admission controller
+			# TYPE nginx_ingress_controller_admission_render_ingresses gauge
+			# HELP nginx_ingress_controller_admission_tested_duration The processing duration of the admission controller tests (float seconds)
+			# TYPE nginx_ingress_controller_admission_tested_duration gauge
+		`
+		metadataThird = `
+			# HELP nginx_ingress_controller_admission_config_size The size of the tested configuration
+			# TYPE nginx_ingress_controller_admission_config_size gauge
+			# HELP nginx_ingress_controller_admission_render_duration The processing duration of ingresses rendering by the admission controller (float seconds)
+			# TYPE nginx_ingress_controller_admission_render_duration gauge
+			# HELP nginx_ingress_controller_admission_render_ingresses The length of ingresses rendered by the admission controller
+			# TYPE nginx_ingress_controller_admission_render_ingresses gauge
+			# HELP nginx_ingress_controller_admission_roundtrip_duration The complete duration of the admission controller at the time to process a new event (float seconds)
+			# TYPE nginx_ingress_controller_admission_roundtrip_duration gauge
+			# HELP nginx_ingress_controller_admission_tested_ingresses The length of ingresses processed by the admission controller
+			# TYPE nginx_ingress_controller_admission_tested_ingresses gauge
+			# HELP nginx_ingress_controller_admission_tested_duration The processing duration of the admission controller tests (float seconds)
+			# TYPE nginx_ingress_controller_admission_tested_duration gauge
+		`
+	)
+	cases := []struct {
+		name    string
+		test    func(*AdmissionCollector)
+		metrics []string
+		want    string
+	}{
+		{
+			name: "should return 0 as values on a fresh initiated collector",
+			test: func(am *AdmissionCollector) {
+			},
+			want: metadataFirst + `
+				nginx_ingress_controller_admission_config_size{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 0
+				nginx_ingress_controller_admission_roundtrip_duration{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 0
+			`,
+			metrics: []string{"nginx_ingress_controller_admission_config_size", "nginx_ingress_controller_admission_roundtrip_duration"},
+		},
+		{
+			name: "set admission metrics to 1 in all fields and validate next set",
+			test: func(am *AdmissionCollector) {
+				am.SetAdmissionMetrics(1, 1, 1, 1, 1, 1)
+			},
+			want: metadataSecond + `
+				nginx_ingress_controller_admission_render_ingresses{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 1
+				nginx_ingress_controller_admission_tested_duration{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 1
+			`,
+			metrics: []string{"nginx_ingress_controller_admission_render_ingresses", "nginx_ingress_controller_admission_tested_duration"},
+		},
+		{
+			name: "set admission metrics to 5 in all fields and validate all sets",
+			test: func(am *AdmissionCollector) {
+				am.SetAdmissionMetrics(5, 5, 5, 5, 5, 5)
+			},
+			want: metadataThird + `
+				nginx_ingress_controller_admission_config_size{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 5
+				nginx_ingress_controller_admission_render_duration{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 5
+				nginx_ingress_controller_admission_render_ingresses{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 5
+				nginx_ingress_controller_admission_roundtrip_duration{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 5
+				nginx_ingress_controller_admission_tested_ingresses{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 5
+				nginx_ingress_controller_admission_tested_duration{controller_class="nginx",controller_namespace="default",controller_pod="pod"} 5
+			`,
+			metrics: []string{
+				"nginx_ingress_controller_admission_config_size",
+				"nginx_ingress_controller_admission_render_duration",
+				"nginx_ingress_controller_admission_render_ingresses",
+				"nginx_ingress_controller_admission_roundtrip_duration",
+				"nginx_ingress_controller_admission_tested_ingresses",
+				"nginx_ingress_controller_admission_tested_duration",
+			},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			am := NewAdmissionCollector("pod", "default", "nginx")
+			reg := prometheus.NewPedanticRegistry()
+			if err := reg.Register(am); err != nil {
+				t.Errorf("registering collector failed: %s", err)
+			}
+
+			c.test(am)
+
+			if err := GatherAndCompare(am, c.want, c.metrics, reg); err != nil {
+				t.Errorf("unexpected collecting result:\n%s", err)
+			}
+
+			reg.Unregister(am)
+		})
+	}
+}
diff --git a/test/e2e/annotations/cors.go b/test/e2e/annotations/cors.go
index e4cdd32b4..c17eb0b20 100644
--- a/test/e2e/annotations/cors.go
+++ b/test/e2e/annotations/cors.go
@@ -44,10 +44,12 @@ var _ = framework.DescribeAnnotation("cors-*", func() {
 		f.WaitForNginxServer(host,
 			func(server string) bool {
 				return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS';") &&
-					strings.Contains(server, "more_set_headers 'Access-Control-Allow-Origin: *';") &&
+					strings.Contains(server, "more_set_headers 'Access-Control-Allow-Origin: $http_origin';") &&
 					strings.Contains(server, "more_set_headers 'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';") &&
 					strings.Contains(server, "more_set_headers 'Access-Control-Max-Age: 1728000';") &&
-					strings.Contains(server, "more_set_headers 'Access-Control-Allow-Credentials: true';")
+					strings.Contains(server, "more_set_headers 'Access-Control-Allow-Credentials: true';") &&
+					strings.Contains(server, "set $http_origin *;") &&
+					strings.Contains(server, "$cors 'true';")
 			})
 
 		f.HTTPTestClient().
@@ -107,6 +109,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() {
 
 	ginkgo.It("should allow origin for cors", func() {
 		host := "cors.foo.com"
+		origin := "https://origin.cors.com:8080"
 		annotations := map[string]string{
 			"nginx.ingress.kubernetes.io/enable-cors":       "true",
 			"nginx.ingress.kubernetes.io/cors-allow-origin": "https://origin.cors.com:8080",
@@ -115,10 +118,20 @@ var _ = framework.DescribeAnnotation("cors-*", func() {
 		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
 		f.EnsureIngress(ing)
 
-		f.WaitForNginxServer(host,
-			func(server string) bool {
-				return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Origin: https://origin.cors.com:8080';")
-			})
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin})
 	})
 
 	ginkgo.It("should allow headers for cors", func() {
@@ -152,4 +165,450 @@ var _ = framework.DescribeAnnotation("cors-*", func() {
 				return strings.Contains(server, "more_set_headers 'Access-Control-Expose-Headers: X-CustomResponseHeader, X-CustomSecondHeader';")
 			})
 	})
+
+	ginkgo.It("should allow - single origin for multiple cors values", func() {
+		host := "cors.foo.com"
+		origin := "https://origin.cors.com:8080"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "https://origin.cors.com:8080, https://origin2.cors.com",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin})
+	})
+
+	ginkgo.It("should not allow - single origin for multiple cors values", func() {
+		host := "cors.foo.com"
+		origin := "http://no.origin.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://origin2.cors.com, https://origin.com",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+	})
+
+	ginkgo.It("should allow correct origins - single origin for multiple cors values", func() {
+		host := "cors.foo.com"
+		badOrigin := "origin.cors.com:8080"
+		origin1 := "https://origin2.cors.com"
+		origin2 := "https://origin.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "origin.cors.com:8080, https://origin2.cors.com, https://origin.com",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", badOrigin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin1).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin1).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin1})
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin2).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin2).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin2})
+	})
+
+	ginkgo.It("should not break functionality", func() {
+		host := "cors.foo.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "*",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{"*"})
+	})
+
+	ginkgo.It("should not break functionality - without `*`", func() {
+		host := "cors.foo.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors": "true",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{"*"})
+	})
+
+	ginkgo.It("should not break functionality with extra domain", func() {
+		host := "cors.foo.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "*, foo.bar.com",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{"*"})
+	})
+
+	ginkgo.It("should not match", func() {
+		host := "cors.foo.com"
+		origin := "https://fooxbar.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "https://foo.bar.com",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+	})
+
+	ginkgo.It("should allow - single origin with required port", func() {
+		host := "cors.foo.com"
+		origin := "http://origin.com:8080"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://origin.cors.com:8080, http://origin.com:8080",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin})
+	})
+
+	ginkgo.It("should not allow - single origin with port and origin without port", func() {
+		host := "cors.foo.com"
+		origin := "http://origin.com:8080"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "https://origin2.cors.com, http://origin.com",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+	})
+
+	ginkgo.It("should not allow - single origin without port and origin with required port", func() {
+		host := "cors.foo.com"
+		origin := "http://origin.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://origin.cors.com:8080, http://origin.com:8080",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+	})
+
+	ginkgo.It("should allow - matching origin with wildcard origin (2 subdomains)", func() {
+		host := "cors.foo.com"
+		origin := "http://foo.origin.cors.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://*.origin.cors.com, http://*.origin.com:8080",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin})
+	})
+
+	ginkgo.It("should not allow - unmatching origin with wildcard origin (2 subdomains)", func() {
+		host := "cors.foo.com"
+		origin := "http://bar.foo.origin.cors.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://*.origin.cors.com, http://*.origin.com:8080",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+	})
+
+	ginkgo.It("should allow - matching origin+port with wildcard origin", func() {
+		host := "cors.foo.com"
+		origin := "http://abc.origin.com:8080"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://origin.cors.com:8080, http://*.origin.com:8080",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin})
+	})
+
+	ginkgo.It("should not allow - portless origin with wildcard origin", func() {
+		host := "cors.foo.com"
+		origin := "http://abc.origin.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://origin.cors.com:8080, http://*.origin.com:8080",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+	})
+
+	ginkgo.It("should allow correct origins - missing subdomain + origin with wildcard origin and correct origin", func() {
+		host := "cors.foo.com"
+		badOrigin := "http://origin.com:8080"
+		origin := "http://bar.origin.com:8080"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "http://origin.cors.com:8080, http://*.origin.com:8080",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", badOrigin).
+			Expect().
+			Headers().NotContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{origin})
+	})
+
+	ginkgo.It("should allow - missing origins (should allow all origins)", func() {
+		host := "cors.foo.com"
+		origin := "http://origin.com"
+		origin2 := "http://book.origin.com"
+		origin3 := "test.origin.com"
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-cors":       "true",
+			"nginx.ingress.kubernetes.io/cors-allow-origin": "      ",
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		// the client should still receive a response but browsers should block the request
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{"*"})
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin2).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin2).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{"*"})
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin3).
+			Expect().
+			Headers().ContainsKey("Access-Control-Allow-Origin")
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("Origin", origin3).
+			Expect().
+			Status(http.StatusOK).Headers().
+			ValueEqual("Access-Control-Allow-Origin", []string{"*"})
+	})
 })
diff --git a/test/e2e/settings/brotli.go b/test/e2e/settings/brotli.go
new file mode 100644
index 000000000..52092ee83
--- /dev/null
+++ b/test/e2e/settings/brotli.go
@@ -0,0 +1,74 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package settings
+
+import (
+	"fmt"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"github.com/onsi/ginkgo"
+
+	"k8s.io/ingress-nginx/test/e2e/framework"
+)
+
+var _ = framework.IngressNginxDescribe("brotli", func() {
+	f := framework.NewDefaultFramework("brotli")
+
+	host := "brotli"
+
+	ginkgo.BeforeEach(func() {
+		f.NewHttpbinDeployment()
+	})
+
+	ginkgo.It("should only compress responses that meet the `brotli-min-length` condition", func() {
+		brotliMinLength := 24
+		contentEncoding := "application/octet-stream"
+		f.UpdateNginxConfigMapData("enable-brotli", "true")
+		f.UpdateNginxConfigMapData("brotli-types", contentEncoding)
+		f.UpdateNginxConfigMapData("brotli-min-length", strconv.Itoa(brotliMinLength))
+
+		f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBinService, 80, nil))
+
+		f.WaitForNginxConfiguration(
+			func(server string) bool {
+				return strings.Contains(server, fmt.Sprintf("server_name %v", host)) &&
+					strings.Contains(server, "brotli on") &&
+					strings.Contains(server, fmt.Sprintf("brotli_types %v", contentEncoding)) &&
+					strings.Contains(server, fmt.Sprintf("brotli_min_length %d", brotliMinLength))
+			})
+
+		f.HTTPTestClient().
+			GET(fmt.Sprintf("/bytes/%d", brotliMinLength)).
+			WithHeader("Host", host).
+			WithHeader("Accept-Encoding", "br").
+			Expect().
+			Status(http.StatusOK).
+			ContentType(contentEncoding).
+			ContentEncoding("br")
+
+		f.HTTPTestClient().
+			GET(fmt.Sprintf("/bytes/%d", brotliMinLength-1)).
+			WithHeader("Host", host).
+			WithHeader("Accept-Encoding", "br").
+			Expect().
+			Status(http.StatusOK).
+			ContentType(contentEncoding).
+			ContentEncoding()
+	})
+})