Repository: kubernetes/ingress-nginx
Pull request #7830 (2021-10-21 00:17:24)

Remove double spacing in configmap data
<!--- 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: 

When adding custom values to the controller configmap, blank line are added between the data items. This PR adds the missing `{{-` to the opening `{{` to prevent the templating engine from adding an extra newline.
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [x] 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)
- [ ] Documentation only


## How Has This Been Tested?

I used `helm template` to generate the chart with custom values added to the `controller.config` block in `values.yaml` and observed that the extra line spacing was no longer generated.
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->

## 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! -->
- [ ] My change requires a change to the documentation.
- [ ] 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.
- [x] All new and existing tests passed.


k8s-ci-robot: @JenniferMack: 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: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/ingress-nginx/pull/7830#" title="Author self-approved">JenniferMack</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **cpanato** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @cpanato` 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:

- **[charts/ingress-nginx/OWNERS](https://github.com/kubernetes/ingress-nginx/blob/main/charts/ingress-nginx/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":["cpanato"]} -->

Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index c39d6e2c2..66fba1fbf 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -43,18 +43,19 @@ kubectl exec -it $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --vers
 - **OS** (e.g. from /etc/os-release):
 - **Kernel** (e.g. `uname -a`):
 - **Install tools**:
-  - `Please mention how/where was clsuter created like kubeadm/kops/minikube/kind etc. `
+  - `Please mention how/where was the cluster created like kubeadm/kops/minikube/kind etc. `
 - **Basic cluster related info**:
   - `kubectl version`
   - `kubectl get nodes -o wide`
 
 - **How was the ingress-nginx-controller installed**:
-  - If helm was used then please show output of `helm ls -A`
+  - If helm was used then please show output of `helm ls -A | grep -i ingress`
   - If helm was used then please show output of `helm -n <ingresscontrollernamepspace> get values <helmreleasename>`
-  - If helm was not used, then please explain how the ingress-nginx-controller was installed or copy/paste the command used to install the controller below
+  - If helm was not used, then copy/paste the complete precise command used to install the controller, along with the flags and options used
   - if you have more than one instance of the ingress-nginx-controller installed in the same cluster, please provide details for all the instances
 
 - **Current State of the controller**:
+  - `kubectl describe ingressclasses`
   - `kubectl -n <ingresscontrollernamespace> get all -A -o wide`
   - `kubectl -n <ingresscontrollernamespace> describe po <ingresscontrollerpodname>`
   - `kubectl -n <ingresscontrollernamespace> describe svc <ingresscontrollerservicename>`
@@ -100,7 +101,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 +110,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/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 771804b54..b4041a579 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -25,6 +25,9 @@ blocks.  If they're super-long, please use the details tag like
 
 <!-- Does it require a particular kubernetes version? -->
 
-<!-- If this is actually about documentation, add `/kind documentation` below -->
+<!-- If this is actually about documentation, uncomment the following block -->
 
-/kind feature
+<!-- 
+/kind documentation
+/remove-kind feature
+-->
\ No newline at end of file
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/OWNERS_ALIASES b/OWNERS_ALIASES
index b313f36b8..f147d0f89 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -30,3 +30,6 @@ aliases:
   ingress-nginx-helm-reviewers:
   - ChiefAlexander
   - cpanato
+
+  ingress-nginx-kube-webhook-certgen-reviewers:
+  - invidian
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/charts/ingress-nginx/templates/NOTES.txt b/charts/ingress-nginx/templates/NOTES.txt
index 29aeb113a..2dbf14f21 100644
--- a/charts/ingress-nginx/templates/NOTES.txt
+++ b/charts/ingress-nginx/templates/NOTES.txt
@@ -32,8 +32,6 @@ An example Ingress that makes use of the controller:
   apiVersion: networking.k8s.io/v1
   kind: Ingress
   metadata:
-    annotations:
-      kubernetes.io/ingress.class: {{ .Values.controller.ingressClassResource.name }}
     name: example
     namespace: foo
   spec:
diff --git a/charts/ingress-nginx/templates/controller-configmap.yaml b/charts/ingress-nginx/templates/controller-configmap.yaml
index 697389207..5d618420f 100644
--- a/charts/ingress-nginx/templates/controller-configmap.yaml
+++ b/charts/ingress-nginx/templates/controller-configmap.yaml
@@ -21,6 +21,6 @@ data:
   ssl-dh-param: {{ printf "%s/%s" .Release.Namespace (include "ingress-nginx.controller.fullname" .) }}
 {{- end }}
 {{- range $key, $value := .Values.controller.config }}
-    {{ $key | nindent 2 }}: {{ $value | quote }}
+    {{- $key | nindent 2 }}: {{ $value | quote }}
 {{- end }}
 
diff --git a/charts/ingress-nginx/templates/controller-deployment.yaml b/charts/ingress-nginx/templates/controller-deployment.yaml
index 41276c6fa..cdf96600c 100644
--- a/charts/ingress-nginx/templates/controller-deployment.yaml
+++ b/charts/ingress-nginx/templates/controller-deployment.yaml
@@ -54,7 +54,7 @@ spec:
       imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }}
     {{- end }}
     {{- if .Values.controller.priorityClassName }}
-      priorityClassName: {{ .Values.controller.priorityClassName }}
+      priorityClassName: {{ .Values.controller.priorityClassName | quote }}
     {{- end }}
     {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }}
       securityContext:
diff --git a/charts/ingress-nginx/templates/controller-servicemonitor.yaml b/charts/ingress-nginx/templates/controller-servicemonitor.yaml
index 17894c8be..cf383f0f8 100644
--- a/charts/ingress-nginx/templates/controller-servicemonitor.yaml
+++ b/charts/ingress-nginx/templates/controller-servicemonitor.yaml
@@ -27,10 +27,6 @@ spec:
 {{- end }}
 {{- if .Values.controller.metrics.serviceMonitor.namespaceSelector }}
   namespaceSelector: {{ toYaml .Values.controller.metrics.serviceMonitor.namespaceSelector | nindent 4 }}
-{{ else }}
-  namespaceSelector:
-    matchNames:
-      - {{ .Release.Namespace }}
 {{- end }}
 {{- if .Values.controller.metrics.serviceMonitor.targetLabels }}
   targetLabels:
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/deploy/static/provider/aws/deploy-tls-termination.yaml b/deploy/static/provider/aws/deploy-tls-termination.yaml
index 99e6397c3..a0e07e952 100644
--- a/deploy/static/provider/aws/deploy-tls-termination.yaml
+++ b/deploy/static/provider/aws/deploy-tls-termination.yaml
@@ -269,12 +269,11 @@ apiVersion: v1
 kind: Service
 metadata:
   annotations:
-    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
     service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '60'
     service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: 'true'
     service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-west-2:XXXXXXXX:certificate/XXXXXX-XXXXXXX-XXXXXXX-XXXXXXXX
     service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https
-    service.beta.kubernetes.io/aws-load-balancer-type: elb
+    service.beta.kubernetes.io/aws-load-balancer-type: nlb
   labels:
     helm.sh/chart: ingress-nginx-4.0.6
     app.kubernetes.io/name: ingress-nginx
diff --git a/docs/deploy/index.md b/docs/deploy/index.md
index 3a53aca1c..4dea7d985 100644
--- a/docs/deploy/index.md
+++ b/docs/deploy/index.md
@@ -1,57 +1,49 @@
 # Installation Guide
 
-!!! attention
-    The default configuration watches Ingress object from **all namespaces**.
-
-    To change this behavior use the flag `--watch-namespace` to limit the scope to a particular namespace.
-
-!!! warning
-    If multiple Ingresses define paths for the same host, the ingress controller **merges the definitions**.
-
-!!! danger
-    The [admission webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) requires connectivity between Kubernetes API server and the ingress controller.
-
-    In case [Network policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) or additional firewalls, please allow access to port `8443`.
+There are multiple ways to install the NGINX ingress controller:
+- with [Helm](https://helm.sh), using the project repository chart;
+- with `kubectl apply`, using YAML manifests;
+- with specific addons (e.g. for [minikube](#minikube) or [MicroK8s](#microk8s)).
 
-!!! attention
-    The first time the ingress controller starts, two [Jobs](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/) create the SSL Certificate used by the admission webhook.
-    For this reason, there is an initial delay of up to two minutes until it is possible to create and validate Ingress definitions. 
-
-You can wait until it is ready to run the next command:
-
-```yaml
- kubectl wait --namespace ingress-nginx \
-  --for=condition=ready pod \
-  --selector=app.kubernetes.io/component=controller \
-  --timeout=120s
-```
+On most Kubernetes clusters, the ingress controller will work without requiring any extra configuration. If you want to get started as fast as possible, you can check the [quick start](#quick-start) instructions. However, in many environments, you can improve the performance or get better logs by enabling extra features. we recommend that you check the [environment-specific instructions](#environment-specific-instructions) for details about optimizing the ingress controller for your particular environment or cloud provider.
 
 ## Contents
 
-- [Provider Specific Steps](#provider-specific-steps)
+<!-- Quick tip: run `grep '^##' index.md` to check that the table of contents is up to date. -->
+
+- [Quick start](#quick-start)
+- [Environment-specific instructions](#environment-specific-instructions)
   - [Docker Desktop](#docker-desktop)
   - [minikube](#minikube)
-  - [microk8s](#microk8s)
+  - [MicroK8s](#microk8s)
   - [AWS](#aws)
   - [GCE - GKE](#gce-gke)
   - [Azure](#azure)
   - [Digital Ocean](#digital-ocean)
   - [Scaleway](#scaleway)
   - [Exoscale](#exoscale)
-  - [Oracle Cloud Infrastructure](#oracle-cloud-infrastructure)  
+  - [Oracle Cloud Infrastructure](#oracle-cloud-infrastructure)
   - [Bare-metal](#bare-metal)
-  - [Verify installation](#verify-installation)
-  - [Detect installed version](#detect-installed-version)
-- [Using Helm](#using-helm)
+- [Miscellaneous](#miscellaneous)
 
-### Provider Specific Steps
+## Quick start
 
-#### Docker Desktop
+You can deploy the ingress controller with the following command:
 
-Kubernetes is available in Docker Desktop
+```console
+helm upgrade --install ingress-nginx ingress-nginx \
+  --repo https://kubernetes.github.io/ingress-nginx \
+  --namespace ingress-nginx --create-namespace
+```
 
-- Mac, from [version 18.06.0-ce](https://docs.docker.com/docker-for-mac/release-notes/#stable-releases-of-2018)
-- Windows, from [version 18.06.0-ce](https://docs.docker.com/docker-for-windows/release-notes/#docker-community-edition-18060-ce-win70-2018-07-25)
+It will install the controller in the `ingress-nginx` namespace, creating that namespace if it doesn't already exist.
+
+!!! info
+    This command is *idempotent*:
+    - if the ingress controller is not installed, it will install it,
+    - if the ingress controller is already installed, it will upgrade it.
+
+This requires Helm version 3. If you prefer to use a YAML manifest, you can run the following command instead:
 
 !!! attention
     Before running the command at your terminal, make sure Kubernetes is enabled at Docker settings
@@ -60,35 +52,128 @@ Kubernetes is available in Docker Desktop
 kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.4/deploy/static/provider/cloud/deploy.yaml
 ```
 
+!!! info
+    The YAML manifest in the command above was generated with `helm template`, so you will end up with almost the same resources as if you had used Helm to install the controller.
+
+If you are running an old version of Kubernetes (1.18 or earlier), please read
+[this paragraph](#running-on-Kubernetes-versions-older-than-1.19) for specific instructions.
+
+### Pre-flight check
+
+A few pods should start in the `ingress-nginx` namespace:
+
+```console
+kubectl get pods --namespace=ingress-nginx
+```
+
+After a while, they should all be running. The following command will wait for the ingress controller pod to be up, running, and ready:
+
+```console
+kubectl wait --namespace ingress-nginx \
+  --for=condition=ready pod \
+  --selector=app.kubernetes.io/component=controller \
+  --timeout=120s
+```
+
+### Local testing
+
+Let's create a simple web server and the associated service:
+
+```console
+kubectl create deployment demo --image=httpd --port=80
+kubectl expose deployment demo
+```
+
+Then create an ingress resource. The following example uses an host that maps to `localhost`:
+
+```console
+kubectl create ingress demo-localhost --class=nginx \
+  --rule=demo.localdev.me/*=demo:80
+```
+
+Now, forward a local port to the ingress controller:
+
+```console
+kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80
+```
+
+At this point, if you access http://demo.localdev.me:8080/, you should see an HTML page telling you "It works!".
+
+### Online testing
+
+If your Kubernetes cluster is a "real" cluster that supports services of type `LoadBalancer`, it will have allocated an external IP address or FQDN to the ingress controller.
+
+You can see that IP address or FQDN with the following command:
+
+```console
+kubectl get service ingress-nginx-controller --namespace=ingress-nginx
+```
+
+Set up a DNS record pointing to that IP address or FQDN; then create an ingress resource. The following example assumes that you have set up a DNS record for `www.demo.io`:
+
+```console
+kubectl create ingress demo --class=nginx \
+  --rule=www.demo.io/*=demo:80
+```
+
+You should then be able to see the "It works!" page when you connect to http://www.demo.io/. Congratulations, you are serving a public web site hosted on a Kubernetes cluster! 🎉
+
+## Environment-specific instructions
+
+### Local development clusters
+
 #### minikube
 
-For standard usage:
+The ingress controller can be installed through minikube's addons system:
 
 ```console
 minikube addons enable ingress
 ```
 
-#### microk8s
+#### MicroK8s
 
-For standard usage:
+The ingress controller can be installed through MicroK8s's addons system:
 
 ```console
 microk8s enable ingress
 ```
 
-Please check the microk8s [documentation page](https://microk8s.io/docs/addon-ingress)
+Please check the MicroK8s [documentation page](https://microk8s.io/docs/addon-ingress) for details.
+
+#### Docker Desktop
+
+Kubernetes is available in Docker Desktop:
+
+- Mac, from [version 18.06.0-ce](https://docs.docker.com/docker-for-mac/release-notes/#stable-releases-of-2018)
+- Windows, from [version 18.06.0-ce](https://docs.docker.com/docker-for-windows/release-notes/#docker-community-edition-18060-ce-win70-2018-07-25)
+
+The ingress controller can be installed on Docker Desktop using the default [quick start](#quick-start) instructions.
+
+On most systems, if you don't have any other service of type `LoadBalancer` bound to port 80, the ingress controller will be assigned the `EXTERNAL-IP` of `localhost`, which means that it will be reachable on localhost:80. If that doesn't work, you might have to fall back to the `kubectl port-forward` method described in the [local testing section](#local-testing).
+
+### Cloud deployments
+
+If the load balancers of your cloud provider do active healthchecks on their backends (most do), you can change the `externalTrafficPolicy` of the ingress controller Service to `Local` (instead of the default `Cluster`) to save an extra hop in some cases. If you're installing with Helm, this can be done by adding `--set controller.service.externalTrafficPolicy=Local` to the `helm install` or `helm upgrade` command.
+
+Furthermore, if the load balancers of your cloud provider support the PROXY protocol, you can enable it, and it will let the ingress controller see the real IP address of the clients. Otherwise, it will generally see the IP address of the upstream load balancer. This must be done both in the ingress controller (with e.g. `--set controller.config.use-proxy-protocol=true`) and in the cloud provider's load balancer configuration to function correctly.
+
+In the following sections, we provide YAML manifests that enable these options when possible, using the specific options of various cloud providers.
 
 #### AWS
 
 In AWS we use a Network load balancer (NLB) to expose the NGINX Ingress controller behind a Service of `Type=LoadBalancer`.
 
+!!! info
+    The provided templates illustrate the setup for legacy in-tree service load balancer for AWS NLB.
+    AWS provides the documentation on how to use [Network load balancing on Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/network-load-balancing.html) with [AWS Load Balancer Controller](https://github.com/kubernetes-sigs/aws-load-balancer-controller).
+
 ##### Network Load Balancer (NLB)
 
 ```console
 kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.4/deploy/static/provider/aws/deploy.yaml
 ```
 
-##### TLS termination in AWS Load Balancer (ELB)
+##### TLS termination in AWS Load Balancer (NLB)
 
 In some scenarios is required to terminate TLS in the Load Balancer and not in the ingress controller.
 
@@ -185,9 +270,9 @@ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/cont
 
 A [complete list of available annotations for Oracle Cloud Infrastructure](https://github.com/oracle/oci-cloud-controller-manager/blob/master/docs/load-balancer-annotations.md) can be found in the [OCI Cloud Controller Manager](https://github.com/oracle/oci-cloud-controller-manager) documentation.
 
-#### Bare-metal
+### Bare metal clusters
 
-Using [NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport):
+Using [NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#nodeport):
 
 ```console
 kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.4/deploy/static/provider/baremetal/deploy.yaml
@@ -199,50 +284,58 @@ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/cont
 !!! info
     For extended notes regarding deployments on bare-metal, see [Bare-metal considerations](./baremetal.md).
 
-### Verify installation
+## Miscellaneous
+
+### Checking ingress controller version
 
-To check if the ingress controller pods have started, run the following command:
+Run `nginx-ingress-controller --version` within the pod, for instance with `kubectl exec`:
 
 ```console
-kubectl get pods -n ingress-nginx \
-  -l app.kubernetes.io/name=ingress-nginx --watch
+POD_NAMESPACE=ingress-nginx
+POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o name)
+kubectl exec $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version
 ```
 
-Once the ingress controller pods are running, you can cancel the command typing `Ctrl+C`.
+### Scope
 
-Now, you are ready to create your first ingress.
+By default, the controller watches Ingress objects from all namespaces. If you want to change this behavior, use the flag `--watch-namespace` or check the Helm chart value `controller.scope` to limit the controller to a single namespace.
 
-### Detect installed version
+See also [“How to easily install multiple instances of the Ingress NGINX controller in the same cluster”](https://kubernetes.github.io/ingress-nginx/#how-to-easily-install-multiple-instances-of-the-ingress-nginx-controller-in-the-same-cluster) for more details.
 
-To detect which version of the ingress controller is running, exec into the pod and run `nginx-ingress-controller --version`.
+### Webhook network access
 
-```console
-POD_NAMESPACE=ingress-nginx
-POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}')
-
-kubectl exec -it $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version
-```
+!!! warning
+    The controller uses an [admission webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) to validate Ingress definitions. Make sure that you don't have [Network policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) or additional firewalls preventing connections from the API server to the `ingress-nginx-controller-admission` service.
 
-## Using Helm
+### Certificate generation
 
 !!! attention
-    Only Helm v3 is supported
+    The first time the ingress controller starts, two [Jobs](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/) create the SSL Certificate used by the admission webhook.
 
-NGINX Ingress controller can be installed via [Helm](https://helm.sh/) using the chart from the project repository.
-To install the chart with the release name `ingress-nginx`:
+THis can cause an initial delay of up to two minutes until it is possible to create and validate Ingress definitions.
 
-```console
-helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
-helm repo update
+You can wait until it is ready to run the next command:
 
-helm install ingress-nginx ingress-nginx/ingress-nginx
+```yaml
+ kubectl wait --namespace ingress-nginx \
+  --for=condition=ready pod \
+  --selector=app.kubernetes.io/component=controller \
+  --timeout=120s
 ```
 
-[For multiple NGINX Ingress controllers](https://kubernetes.github.io/ingress-nginx/#how-to-easily-install-multiple-instances-of-the-ingress-nginx-controller-in-the-same-cluster)
+### Running on Kubernetes versions older than 1.19
 
-## Detect installed version:
+Ingress resources evolved over time. They started with `apiVersion: extensions/v1beta1`, then moved to `apiVersion: networking.k8s.io/v1beta1` and more recently to `apiVersion: networking.k8s.io/v1`.
 
-```console
-POD_NAME=$(kubectl get pods -l app.kubernetes.io/name=ingress-nginx -o jsonpath='{.items[0].metadata.name}')
-kubectl exec -it $POD_NAME -- /nginx-ingress-controller --version
-```
+Here is how these Ingress versions are supported in Kubernetes:
+- before Kubernetes 1.19, only `v1beta1` Ingress resources are supported
+- from Kubernetes 1.19 to 1.21, both `v1beta1` and `v1` Ingress resources are supported
+- in Kubernetes 1.22 and above, only `v1` Ingress resources are supported
+
+And here is how these Ingress versions are supported in NGINX Ingress Controller:
+- before version 1.0, only `v1beta1` Ingress resources are supported
+- in version 1.0 and above, only `v1` Ingress resources are
+
+As a result, if you're running Kubernetes 1.19 or later, you should be able to use the latest version of the NGINX Ingress Controller; but if you're using an old version of Kubernetes (1.18 or earlier) you will have to use version 0.X of the NGINX Ingress Controller (e.g. version 0.49).
+
+The Helm chart of the NGINX Ingress Controller switched to version 1 in version 4 of the chart. In other words, if you're running Kubernetes 1.19 or earlier, you should use version 3.X of the chart (this can be done by adding `--version='<4'` to the `helm install` command).
diff --git a/docs/deploy/upgrade.md b/docs/deploy/upgrade.md
index b42a09865..933829a80 100644
--- a/docs/deploy/upgrade.md
+++ b/docs/deploy/upgrade.md
@@ -41,11 +41,11 @@ For interactive editing, use `kubectl edit deployment nginx-ingress-controller -
 
 ## With Helm
 
-If you installed ingress-nginx using the Helm command in the deployment docs so its name is `ngx-ingress`,
+If you installed ingress-nginx using the Helm command in the deployment docs so its name is `ingress-nginx`,
 you should be able to upgrade using
 
 ```shell
-helm upgrade --reuse-values ngx-ingress ingress-nginx/ingress-nginx
+helm upgrade --reuse-values ingress-nginx ingress-nginx/ingress-nginx
 ```
 
 ### Migrating from stable/nginx-ingress
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/grpc/README.md b/docs/examples/grpc/README.md
index 8835b5110..a20c8c7dd 100644
--- a/docs/examples/grpc/README.md
+++ b/docs/examples/grpc/README.md
@@ -6,7 +6,7 @@ This example demonstrates how to route traffic to a gRPC service through the ngi
 
 1. You have a kubernetes cluster running.
 2. You have a domain name such as `example.com` that is configured to route traffic to the ingress controller.
-3. You have the nginx-ingress controller installed as per docs, with gRPC support.
+3. You have the nginx-ingress controller installed as per docs.
 4. You have a backend application running a gRPC server and listening for TCP traffic.  If you want, you can use <https://github.com/grpc/grpc-go/blob/91e0aeb192456225adf27966d04ada4cf8599915/examples/features/reflection/server/main.go> as an example.
 5. You're also responsible for provisioning an SSL certificate for the ingress. So you need to have a valid SSL certificate, deployed as a Kubernetes secret of type tls, in the same namespace as the gRPC application.
 
@@ -20,7 +20,7 @@ This example demonstrates how to route traffic to a gRPC service through the ngi
 
 - As an example gRPC application, we can use this app <https://github.com/grpc/grpc-go/blob/91e0aeb192456225adf27966d04ada4cf8599915/examples/features/reflection/server/main.go> .
 
-- To create a container image for this app, you can use [this Dockerfile](../../../images/go-grpc-greeter-server/rootfs/Dockerfile). 
+- To create a container image for this app, you can use [this Dockerfile](https://github.com/kubernetes/ingress-nginx/blob/5a52d99ae85cfe5ef9535291b8326b0006e75066/images/go-grpc-greeter-server/rootfs/Dockerfile). 
 
 - If you use the Dockerfile mentioned above, to create a image, then given below is an example of a Kubernetes manifest, to create a deployment resource, that uses that image. If needed, then edit this manifest to suit your needs. Assuming the name of this yaml file is  `deployment.go-grpc-greeter-server.yaml` ;
 
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/index.md b/docs/index.md
index 1154ba32b..6aa2ea81c 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -154,10 +154,12 @@ In this scenario, you need to create multiple ingressClasses (see example one).
 
 - Ingress-Nginx-IngressClass-1 with .spec.controller equals to "k8s.io/ingress-nginx1"
 - Ingress-Nginx-IngressClass-2 with .spec.controller equals to "k8s.io/ingress-nginx2"
+
 When deploying your ingress controllers, you will have to change the `--controller-class` field as follows:
 
-Ingress-Nginx-Controller-nginx1 with `k8s.io/ingress-nginx1`
-Ingress-Nginx-Controller-nginx2 with `k8s.io/ingress-nginx2`
+- Ingress-Nginx-Controller-nginx1 with `k8s.io/ingress-nginx1`
+- Ingress-Nginx-Controller-nginx2 with `k8s.io/ingress-nginx2`
+
 Then, when you create an Ingress Object with IngressClassName = `ingress-nginx2`, it will look for controllers with `controller-class=k8s.io/ingress-nginx2` and as `Ingress-Nginx-Controller-nginx2` is watching objects that points to `ingressClass="k8s.io/ingress-nginx2`, it will serve that object, while `Ingress-Nginx-Controller-nginx1` will ignore the ingress object.
 
 Bear in mind that, if your `Ingress-Nginx-Controller-nginx2` is started with the flag `--watch-ingress-without-class=true`, then it will serve:
diff --git a/docs/user-guide/basic-usage.md b/docs/user-guide/basic-usage.md
index ae913b6a4..64c81148b 100644
--- a/docs/user-guide/basic-usage.md
+++ b/docs/user-guide/basic-usage.md
@@ -2,7 +2,11 @@
 
 ingress-nginx can be used for many use cases, inside various cloud provider and supports a lot of configurations. In this section you can find a common usage scenario where a single load balancer powered by ingress-nginx will route traffic to 2 different HTTP backend services based on the host name.
 
-First of all follow the instructions to install ingress-nginx. Then imagine that you need to expose 2 HTTP services already installed: `myServiceA`, `myServiceB`. Let's say that you want to expose the first at `myServiceA.foo.org` and the second at `myServiceB.foo.org`. One possible solution is to create two **ingress** resources:
+First of all follow the instructions to install ingress-nginx. Then imagine that you need to expose 2 HTTP services already installed, `myServiceA`, `myServiceB`, and configured as `type: ClusterIP`. 
+
+Let's say that you want to expose the first at `myServiceA.foo.org` and the second at `myServiceB.foo.org`.
+
+If cluster version < 1.19 you can create two **ingress** resources like this:
 
 ```
 apiVersion: networking.k8s.io/v1
@@ -46,12 +50,8 @@ spec:
               number: 80
 ```
 
-When you apply this yaml, 2 ingress resources will be created managed by the **ingress-nginx** instance. Nginx is configured to automatically discover all ingress with the `kubernetes.io/ingress.class: "nginx"` annotation.
-Please note that the ingress resource should be placed inside the same namespace of the backend resource.
+If cluster version >= 1.19 the Ingress resource above will not work, instead of annotations you should use the new `ingressClassName: nginx` property.
 
-If cluster version >= 1.19 the Ingress resource above will not work.
-You need to deploy the following so that it match the new api version.
-This will require changes to the Ingress objects as shown below, and a new object from type "kind: IngressClass"
 ```
 apiVersion: networking.k8s.io/v1
 kind: Ingress
@@ -88,17 +88,15 @@ spec:
             port: 
               number: 80
   ingressClassName: nginx
----
-apiVersion: networking.k8s.io/v1
-kind: IngressClass
-metadata:
-  name: nginx
-spec:
-  controller: k8s.io/ingress-nginx
 ```
 
-On many cloud providers ingress-nginx will also create the corresponding Load Balancer resource. All you have to do is get the external IP and add a DNS `A record` inside your DNS provider that point myServiceA.foo.org and myServiceB.foo.org to the nginx external IP. Get the external IP by running:
+When you apply this yaml, 2 ingress resources will be created managed by the **ingress-nginx** instance. Nginx is configured to automatically discover all ingress with the `kubernetes.io/ingress.class: "nginx"` annotation or where `ingressClassName: nginx` is present.
+Please note that the ingress resource should be placed inside the same namespace of the backend resource.
+
+On many cloud providers ingress-nginx will also create the corresponding Load Balancer resource. All you have to do is get the external IP and add a DNS `A record` inside your DNS provider that point myservicea.foo.org and myserviceb.foo.org to the nginx external IP. Get the external IP by running:
 
 ```
 kubectl get services -n ingress-nginx
 ```
+
+To test inside minikube refer to this documentation: [Set up Ingress on Minikube with the NGINX Ingress Controller](https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/)
\ No newline at end of file
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 38439b33f..19bd3947f 100755
--- a/docs/user-guide/nginx-configuration/annotations.md
+++ b/docs/user-guide/nginx-configuration/annotations.md
@@ -112,6 +112,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
 |[nginx.ingress.kubernetes.io/connection-proxy-header](#connection-proxy-header)|string|
 |[nginx.ingress.kubernetes.io/enable-access-log](#enable-access-log)|"true" or "false"|
 |[nginx.ingress.kubernetes.io/enable-opentracing](#enable-opentracing)|"true" or "false"|
+|[nginx.ingress.kubernetes.io/opentracing-trust-incoming-span](#opentracing-trust-incoming-span)|"true" or "false"|
 |[nginx.ingress.kubernetes.io/enable-influxdb](#influxdb)|"true" or "false"|
 |[nginx.ingress.kubernetes.io/influxdb-measurement](#influxdb)|string|
 |[nginx.ingress.kubernetes.io/influxdb-port](#influxdb)|string|
@@ -303,6 +304,8 @@ nginx.ingress.kubernetes.io/configuration-snippet: |
   more_set_headers "Request-Id: $req_id";
 ```
 
+Be aware this can be dangerous in multi-tenant clusters, as it can lead to people with otherwise limited permissions being able to retrieve all secrets on the cluster. The recommended mitigation for this threat is to disable this feature, so it may not work for you. See CVE-2021-25742 and the [related issue on github](https://github.com/kubernetes/ingress-nginx/issues/7837) for more information.
+
 ### Custom HTTP Errors
 
 Like the [`custom-http-errors`](./configmap.md#custom-http-errors) value in the ConfigMap, this annotation will set NGINX `proxy-intercept-errors`, but only for the NGINX location associated with this ingress. If a [default backend annotation](#default-backend) is specified on the ingress, the errors will be routed to that annotation's default backend service (instead of the global default backend).
@@ -351,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.
 
@@ -766,6 +772,15 @@ to enable it or disable it for a specific ingress (e.g. to turn off tracing of e
 nginx.ingress.kubernetes.io/enable-opentracing: "true"
 ```
 
+### Opentracing Trust Incoming Span
+
+The option to trust incoming trace spans can be enabled or disabled globally through the ConfigMap but this will
+sometimes need to be overriden to enable it or disable it for a specific ingress (e.g. only enable on a private endpoint)
+
+```yaml
+nginx.ingress.kubernetes.io/opentracing-trust-incoming-span: "true"
+```
+
 ### X-Forwarded-Prefix Header
 To add the non-standard `X-Forwarded-Prefix` header to the upstream request with a string value, the following annotation can be used:
 
diff --git a/docs/user-guide/nginx-configuration/configmap.md b/docs/user-guide/nginx-configuration/configmap.md
index 4fdda21ba..281b0d510 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|
@@ -311,7 +312,7 @@ _References:_
 
 ## disable-access-log
 
-Disables the Access Log from the entire Ingress Controller. _**default:**_ '"false"'
+Disables the Access Log from the entire Ingress Controller. _**default:**_ `false`
 
 _References:_
 [http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log](http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log)
@@ -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 916b29637..e27f32f6b 100644
--- a/docs/user-guide/third-party-addons/opentracing.md
+++ b/docs/user-guide/third-party-addons/opentracing.md
@@ -46,6 +46,9 @@ opentracing-operation-name
 # specifies specifies the name to use for the location span
 opentracing-location-operation-name
 
+# sets whether or not to trust incoming tracing spans
+opentracing-trust-incoming-span
+
 # specifies the port to use when uploading traces, Default: 9411
 zipkin-collector-port
 
@@ -114,6 +117,15 @@ datadog-sample-rate
 
 All these options (including host) allow environment variables, such as `$HOSTNAME` or `$HOST_IP`. In the case of Jaeger, if you have a Jaeger agent running on each machine in your cluster, you can use something like `$HOST_IP` (which can be 'mounted' with the `status.hostIP` fieldpath, as described [here](https://kubernetes.io/docs/tasks/inject-data-application/downward-api-volume-expose-pod-information/#capabilities-of-the-downward-api)) to make sure traces will be sent to the local agent.
 
+
+Note that you can also set whether to trust incoming spans (global default is true) per-location using annotations like the following:
+```
+kind: Ingress
+metadata:
+  annotations:
+    nginx.ingress.kubernetes.io/opentracing-trust-incoming-span: "true"
+```
+
 ## Examples
 
 The following examples show how to deploy and test different distributed tracing systems. These example can be performed using Minikube.
@@ -177,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/hack/generate-deploy-scripts.sh b/hack/generate-deploy-scripts.sh
index b8aec4104..28625721b 100755
--- a/hack/generate-deploy-scripts.sh
+++ b/hack/generate-deploy-scripts.sh
@@ -89,11 +89,12 @@ controller:
     externalTrafficPolicy: Local
 
     annotations:
-      service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
-      service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: 'true'
+      # This example is for legacy in-tree service load balancer controller for AWS NLB,
+      # that has been phased out from Kubernetes mainline.
+      service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
       service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
       service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-west-2:XXXXXXXX:certificate/XXXXXX-XXXXXXX-XXXXXXX-XXXXXXXX"
-      service.beta.kubernetes.io/aws-load-balancer-type: elb
+      service.beta.kubernetes.io/aws-load-balancer-type: nlb
       # Ensure the ELB idle timeout is less than nginx keep-alive timeout. By default,
       # NGINX keep-alive is set to 75s. If using WebSockets, the value will need to be
       # increased to '3600' to avoid any potential issues.
diff --git a/images/kube-webhook-certgen/OWNERS b/images/kube-webhook-certgen/OWNERS
new file mode 100644
index 000000000..ad47b2e6d
--- /dev/null
+++ b/images/kube-webhook-certgen/OWNERS
@@ -0,0 +1,10 @@
+# See the OWNERS docs: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md
+
+approvers:
+- ingress-nginx-admins
+- ingress-nginx-maintainers
+
+reviewers:
+- ingress-nginx-admins
+- ingress-nginx-maintainers
+- ingress-nginx-kube-webhook-certgen-reviewers
diff --git a/images/nginx/rootfs/build.sh b/images/nginx/rootfs/build.sh
index 919e4fcbb..ab64d5d94 100755
--- a/images/nginx/rootfs/build.sh
+++ b/images/nginx/rootfs/build.sh
@@ -137,12 +137,15 @@ export BUILD_PATH=/tmp/build
 
 ARCH=$(uname -m)
 
-export USE_OPENTELEMETRY=true
 if [[ ${ARCH} == "s390x" ]]; then
   export LUAJIT_VERSION=9d5750d28478abfdcaefdfdc408f87752a21e431
   export LUA_RESTY_CORE=0.1.17
   export LUA_NGX_VERSION=0.10.15
   export LUA_STREAM_NGX_VERSION=0.0.7
+fi
+
+export USE_OPENTELEMETRY=true
+if [[ ${ARCH} == "s390x" ]] || [[ ${ARCH} == "armv7l" ]]; then
   export USE_OPENTELEMETRY=false
 fi
 
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/annotations/opentracing/main.go b/internal/ingress/annotations/opentracing/main.go
index 2ed4a2182..17ba7eb9f 100644
--- a/internal/ingress/annotations/opentracing/main.go
+++ b/internal/ingress/annotations/opentracing/main.go
@@ -29,8 +29,10 @@ type opentracing struct {
 
 // Config contains the configuration to be used in the Ingress
 type Config struct {
-	Enabled bool `json:"enabled"`
-	Set     bool `json:"set"`
+	Enabled      bool `json:"enabled"`
+	Set          bool `json:"set"`
+	TrustEnabled bool `json:"trust-enabled"`
+	TrustSet     bool `json:"trust-set"`
 }
 
 // Equal tests for equality between two Config types
@@ -43,6 +45,14 @@ func (bd1 *Config) Equal(bd2 *Config) bool {
 		return false
 	}
 
+	if bd1.TrustSet != bd2.TrustSet {
+		return false
+	}
+
+	if bd1.TrustEnabled != bd2.TrustEnabled {
+		return false
+	}
+
 	return true
 }
 
@@ -54,8 +64,13 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
 func (s opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
 	enabled, err := parser.GetBoolAnnotation("enable-opentracing", ing)
 	if err != nil {
-		return &Config{Set: false, Enabled: false}, nil
+		return &Config{}, nil
+	}
+
+	trustSpan, err := parser.GetBoolAnnotation("opentracing-trust-incoming-span", ing)
+	if err != nil {
+		return &Config{Set: true, Enabled: enabled}, nil
 	}
 
-	return &Config{Set: true, Enabled: enabled}, nil
+	return &Config{Set: true, Enabled: enabled, TrustSet: true, TrustEnabled: trustSpan}, nil
 }
diff --git a/internal/ingress/controller/config/config.go b/internal/ingress/controller/config/config.go
index a29c1b094..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"`
 
@@ -537,6 +540,11 @@ type Configuration struct {
 	// OpentracingOperationName specifies a custom name for the location span
 	OpentracingLocationOperationName string `json:"opentracing-location-operation-name"`
 
+	// OpentracingTrustIncomingSpan sets whether or not to trust incoming trace spans
+	// If false, incoming span headers will be rejected
+	// Default: true
+	OpentracingTrustIncomingSpan bool `json:"opentracing-trust-incoming-span"`
+
 	// ZipkinCollectorHost specifies the host to use when uploading traces
 	ZipkinCollectorHost string `json:"zipkin-collector-host"`
 
@@ -773,6 +781,7 @@ func NewDefault() Configuration {
 		BlockUserAgents:                  defBlockEntity,
 		BlockReferers:                    defBlockEntity,
 		BrotliLevel:                      4,
+		BrotliMinLength:                  20,
 		BrotliTypes:                      brotliTypes,
 		ClientHeaderBufferSize:           "1k",
 		ClientHeaderTimeout:              60,
@@ -874,6 +883,7 @@ func NewDefault() Configuration {
 		LimitConnZoneVariable:                  defaultLimitConnZoneVariable,
 		BindAddressIpv4:                        defBindAddress,
 		BindAddressIpv6:                        defBindAddress,
+		OpentracingTrustIncomingSpan:           true,
 		ZipkinCollectorPort:                    9411,
 		ZipkinServiceName:                      "nginx",
 		ZipkinSampleRate:                       1.0,
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 e5da5b993..1147c8e5d 100644
--- a/internal/ingress/controller/template/template.go
+++ b/internal/ingress/controller/template/template.go
@@ -63,6 +63,9 @@ const (
 
 // Writer is the interface to render a template
 type Writer interface {
+	// Write renders the template.
+	// NOTE: Implementors must ensure that the content of the returned slice is not modified by the implementation
+	// after the return of this function.
 	Write(conf config.TemplateConfig) ([]byte, error)
 }
 
@@ -202,7 +205,12 @@ func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) {
 		return nil, err
 	}
 
-	return outCmdBuf.Bytes(), nil
+	// make a copy to ensure that we are no longer modifying the content of the buffer
+	out := outCmdBuf.Bytes()
+	res := make([]byte, len(out))
+	copy(res, out)
+
+	return res, nil
 }
 
 var (
@@ -268,6 +276,7 @@ var (
 		"shouldLoadAuthDigestModule":         shouldLoadAuthDigestModule,
 		"shouldLoadInfluxDBModule":           shouldLoadInfluxDBModule,
 		"buildServerName":                    buildServerName,
+		"buildCorsOriginRegex":               buildCorsOriginRegex,
 	}
 )
 
@@ -1440,7 +1449,7 @@ func httpsListener(addresses []string, co string, tc config.TemplateConfig) []st
 	return out
 }
 
-func buildOpentracingForLocation(isOTEnabled bool, location *ingress.Location) string {
+func buildOpentracingForLocation(isOTEnabled bool, isOTTrustSet bool, location *ingress.Location) string {
 	isOTEnabledInLoc := location.Opentracing.Enabled
 	isOTSetInLoc := location.Opentracing.Set
 
@@ -1448,25 +1457,21 @@ func buildOpentracingForLocation(isOTEnabled bool, location *ingress.Location) s
 		if isOTSetInLoc && !isOTEnabledInLoc {
 			return "opentracing off;"
 		}
-
-		opc := opentracingPropagateContext(location)
-		if opc != "" {
-			opc = fmt.Sprintf("opentracing on;\n%v", opc)
-		}
-
-		return opc
+	} else if !isOTSetInLoc || !isOTEnabledInLoc {
+		return ""
 	}
 
-	if isOTSetInLoc && isOTEnabledInLoc {
-		opc := opentracingPropagateContext(location)
-		if opc != "" {
-			opc = fmt.Sprintf("opentracing on;\n%v", opc)
-		}
+	opc := opentracingPropagateContext(location)
+	if opc != "" {
+		opc = fmt.Sprintf("opentracing on;\n%v", opc)
+	}
 
-		return opc
+	if (!isOTTrustSet && !location.Opentracing.TrustSet) ||
+		(location.Opentracing.TrustSet && !location.Opentracing.TrustEnabled) {
+		opc = opc + "\nopentracing_trust_incoming_span off;"
 	}
 
-	return ""
+	return opc
 }
 
 // shouldLoadOpentracingModule determines whether or not the Opentracing module needs to be loaded.
@@ -1672,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 32b02f72a..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 */}}
@@ -1096,7 +1108,7 @@ stream {
             set $location_path  {{ $ing.Path | escapeLiteralDollar | quote }};
             set $global_rate_limit_exceeding n;
 
-            {{ buildOpentracingForLocation $all.Cfg.EnableOpentracing $location }}
+            {{ buildOpentracingForLocation $all.Cfg.EnableOpentracing $all.Cfg.OpentracingTrustIncomingSpan $location }}
 
             {{ if $location.Mirror.Source }}
             mirror {{ $location.Mirror.Source }};


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/annotations/opentracing/main_test.go b/internal/ingress/annotations/opentracing/main_test.go
index 77e29cb5d..7bd9d31ff 100644
--- a/internal/ingress/annotations/opentracing/main_test.go
+++ b/internal/ingress/annotations/opentracing/main_test.go
@@ -106,6 +106,29 @@ func TestIngressAnnotationOpentracingSetFalse(t *testing.T) {
 	}
 }
 
+func TestIngressAnnotationOpentracingTrustSetTrue(t *testing.T) {
+	ing := buildIngress()
+
+	data := map[string]string{}
+	data[parser.GetAnnotationWithPrefix("enable-opentracing")] = "true"
+	data[parser.GetAnnotationWithPrefix("opentracing-trust-incoming-span")] = "true"
+	ing.SetAnnotations(data)
+
+	val, _ := NewParser(&resolver.Mock{}).Parse(ing)
+	openTracing, ok := val.(*Config)
+	if !ok {
+		t.Errorf("expected a Config type")
+	}
+
+	if !openTracing.Enabled {
+		t.Errorf("expected annotation value to be true, got false")
+	}
+
+	if !openTracing.TrustEnabled {
+		t.Errorf("expected annotation value to be true, got false")
+	}
+}
+
 func TestIngressAnnotationOpentracingUnset(t *testing.T) {
 	ing := buildIngress()
 
diff --git a/internal/ingress/controller/template/template_test.go b/internal/ingress/controller/template/template_test.go
index 96dc9acae..cfb65c08e 100644
--- a/internal/ingress/controller/template/template_test.go
+++ b/internal/ingress/controller/template/template_test.go
@@ -1637,32 +1637,44 @@ func TestShouldLoadModSecurityModule(t *testing.T) {
 
 func TestOpentracingForLocation(t *testing.T) {
 	trueVal := true
+	falseVal := false
 
 	loadOT := `opentracing on;
 opentracing_propagate_context;`
+	loadOTUntrustedSpan := `opentracing on;
+opentracing_propagate_context;
+opentracing_trust_incoming_span off;`
 	testCases := []struct {
-		description string
-		globalOT    bool
-		isSetInLoc  bool
-		isOTInLoc   *bool
-		expected    string
+		description     string
+		globalOT        bool
+		isSetInLoc      bool
+		isOTInLoc       *bool
+		globalTrust     bool
+		isTrustSetInLoc bool
+		isTrustInLoc    *bool
+		expected        string
 	}{
-		{"globally enabled, without annotation", true, false, nil, loadOT},
-		{"globally enabled and enabled in location", true, true, &trueVal, loadOT},
-		{"globally disabled and not enabled in location", false, false, nil, ""},
-		{"globally disabled but enabled in location", false, true, &trueVal, loadOT},
-		{"globally disabled, enabled in location but false", false, true, &trueVal, loadOT},
+		{"globally enabled, without annotation", true, false, nil, true, false, nil, loadOT},
+		{"globally enabled and enabled in location", true, true, &trueVal, true, false, nil, loadOT},
+		{"globally disabled and not enabled in location", false, false, nil, true, false, nil, ""},
+		{"globally disabled but enabled in location", false, true, &trueVal, true, false, nil, loadOT},
+		{"globally trusted, not trusted in location", true, false, nil, true, true, &falseVal, loadOTUntrustedSpan},
+		{"not globally trusted, trust set in location", true, false, nil, false, true, &trueVal, loadOT},
+		{"not globally trusted, trust not set in location", true, false, nil, false, false, nil, loadOTUntrustedSpan},
 	}
 
 	for _, testCase := range testCases {
 		il := &ingress.Location{
-			Opentracing: opentracing.Config{Set: testCase.isSetInLoc},
+			Opentracing: opentracing.Config{Set: testCase.isSetInLoc, TrustSet: testCase.isTrustSetInLoc},
 		}
 		if il.Opentracing.Set {
 			il.Opentracing.Enabled = *testCase.isOTInLoc
 		}
+		if il.Opentracing.TrustSet {
+			il.Opentracing.TrustEnabled = *testCase.isTrustInLoc
+		}
 
-		actual := buildOpentracingForLocation(testCase.globalOT, il)
+		actual := buildOpentracingForLocation(testCase.globalOT, testCase.globalTrust, il)
 
 		if testCase.expected != actual {
 			t.Errorf("%v: expected '%v' but returned '%v'", testCase.description, testCase.expected, actual)
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()
+	})
+})
diff --git a/test/e2e/settings/opentracing.go b/test/e2e/settings/opentracing.go
index 0fe4e62f6..3ac16be78 100644
--- a/test/e2e/settings/opentracing.go
+++ b/test/e2e/settings/opentracing.go
@@ -29,7 +29,8 @@ import (
 )
 
 const (
-	enableOpentracing = "enable-opentracing"
+	enableOpentracing            = "enable-opentracing"
+	opentracingTrustIncomingSpan = "opentracing-trust-incoming-span"
 
 	zipkinCollectorHost = "zipkin-collector-host"
 
@@ -81,6 +82,21 @@ var _ = framework.IngressNginxDescribe("Configure OpenTracing", func() {
 			})
 	})
 
+	ginkgo.It("should include opentracing_trust_incoming_span off directive when disabled", func() {
+		config := map[string]string{}
+		config[enableOpentracing] = "true"
+		config[opentracingTrustIncomingSpan] = "false"
+		config[zipkinCollectorHost] = "127.0.0.1"
+		f.SetNginxConfigMapData(config)
+
+		f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil))
+
+		f.WaitForNginxConfiguration(
+			func(cfg string) bool {
+				return strings.Contains(cfg, "opentracing_trust_incoming_span off")
+			})
+	})
+
 	ginkgo.It("should not exists opentracing_operation_name directive when is empty", func() {
 		config := map[string]string{}
 		config[enableOpentracing] = "true"