1
0
mirror of https://github.com/containous/traefik.git synced 2025-12-03 16:23:52 +03:00

Compare commits

...

20 Commits

Author SHA1 Message Date
mmatur
34b91218f4 Merge v3.6 into master 2025-12-01 16:28:00 +01:00
mmatur
b9c2e757c7 Merge current v2.11 into v3.6 2025-12-01 15:12:04 +01:00
Michel Loiseleur
e15c11961f Protect CI against supply chain attack on nodejs 2025-12-01 14:58:05 +01:00
Michel Loiseleur
d7406ccb4b Fix default value of ingress nginx provider in documentation 2025-12-01 14:32:04 +01:00
Shouhei
f9b943a7ee Fix links of Helm chart values reference to providers.kubernetesGateway.enabled 2025-11-27 14:30:05 +01:00
Emile Vauge
042feacf3e Update SECURITY.md to streamline information 2025-11-24 15:32:04 +01:00
Michael
9638c0b8b0 Upgrade k3s version k8s integration tests 2025-11-24 12:56:04 +01:00
Chris Wayne
759e82c3c8 Update SECURITY.md 2025-11-24 11:56:04 +01:00
GreyXor
2d7262515d Bump github.com/quic-go/quic-go to v0.57.0 2025-11-24 10:44:04 +01:00
Michel Loiseleur
ae86cb7d11 Improve the configuration options display of the Kubernetes Nginx provider 2025-11-21 17:48:04 +01:00
Rémi BUISSON
155b29edbe Add documentation about checkNewVersion 2025-11-21 16:09:54 +01:00
Tom Pillot
4ed8f08757 Fix loadbalancer doc for highest random weight 2025-11-21 15:22:04 +01:00
Ashley
44219f9a1c Add documentation for loadbalancer.server.url in Docker and Swarm providers 2025-11-21 15:04:04 +01:00
Gina A.
8bdcd72042 Web UI dashboard improvements 2025-11-21 09:00:05 +01:00
Kevin Pollet
f3e199cd47 Bump golang.org/x/crypto to v0.45.0 2025-11-20 11:30:05 +01:00
Kevin Pollet
9232535cf6 Validate plugin module name
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
2025-11-20 10:50:04 +01:00
Sheddy
00b64f0ca6 Improve Ingress NGINX provider documentation 2025-11-19 10:10:04 +01:00
Romain
f6ea1f473a Prepare release v3.6.2 2025-11-18 17:06:09 +01:00
Romain
e0d9b060cf Deprecate Kubernetes Ingress NGINX provider experimental flag 2025-11-18 16:56:04 +01:00
kevinpollet
2ad42cd0ec Merge branch v3.6 into master 2025-11-07 16:47:21 +01:00
40 changed files with 1235 additions and 514 deletions

View File

@@ -1,6 +1,8 @@
name: Build Web UI name: Build Web UI
on: on:
workflow_call: {} workflow_call: {}
env:
SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS: 360 # 15 days
jobs: jobs:
build-webui: build-webui:
@@ -22,6 +24,12 @@ jobs:
cache: yarn cache: yarn
cache-dependency-path: webui/yarn.lock cache-dependency-path: webui/yarn.lock
- name: Setup safe-chain
working-directory: ./webui
run: |
npm i -g @aikidosec/safe-chain
safe-chain setup-ci
- name: Build webui - name: Build webui
working-directory: ./webui working-directory: ./webui
run: | run: |

View File

@@ -9,6 +9,7 @@ on:
- 'pkg/provider/kubernetes/gateway/**' - 'pkg/provider/kubernetes/gateway/**'
- 'integration/fixtures/gateway-api-conformance/**' - 'integration/fixtures/gateway-api-conformance/**'
- 'integration/gateway_api_conformance_test.go' - 'integration/gateway_api_conformance_test.go'
- 'integration/integration_test.go'
env: env:
GO_VERSION: '1.24' GO_VERSION: '1.24'

View File

@@ -9,6 +9,7 @@ on:
- 'pkg/provider/kubernetes/knative/**' - 'pkg/provider/kubernetes/knative/**'
- 'integration/fixtures/knative/**' - 'integration/fixtures/knative/**'
- 'integration/knative_conformance_test.go' - 'integration/knative_conformance_test.go'
- 'integration/integration_test.go'
env: env:
GO_VERSION: '1.24' GO_VERSION: '1.24'

View File

@@ -79,6 +79,11 @@ jobs:
cache: 'yarn' cache: 'yarn'
cache-dependency-path: webui/yarn.lock cache-dependency-path: webui/yarn.lock
- name: Setup safe-chain
run: |
npm i -g @aikidosec/safe-chain
safe-chain setup-ci
- name: UI unit tests - name: UI unit tests
working-directory: ./webui working-directory: ./webui
env: env:

View File

@@ -1,3 +1,9 @@
## [v3.6.2](https://github.com/traefik/traefik/tree/v3.6.2) (2025-11-18)
[All Commits](https://github.com/traefik/traefik/compare/v3.6.1...v3.6.2)
**Bug fixes:**
- **[k8s/ingress-nginx]** Deprecate Kubernetes Ingress NGINX provider experimental flag ([#12286](https://github.com/traefik/traefik/pull/12286) by [rtribotte](https://github.com/rtribotte))
## [v3.6.1](https://github.com/traefik/traefik/tree/v3.6.1) (2025-11-13) ## [v3.6.1](https://github.com/traefik/traefik/tree/v3.6.1) (2025-11-13)
[All Commits](https://github.com/traefik/traefik/compare/v3.6.0...v3.6.1) [All Commits](https://github.com/traefik/traefik/compare/v3.6.0...v3.6.1)

View File

@@ -1,10 +1,5 @@
# Security Policy # Security Policy
You can join our security mailing list to be aware of the latest announcements from our security team.
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
## Supported Versions ## Supported Versions
- We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year. - We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year.
@@ -17,10 +12,10 @@ We use [Semantic Versioning](https://semver.org/).
| Version | Supported | | Version | Supported |
|-----------|--------------------| |-----------|--------------------|
| `2.2.x` | :white_check_mark: | | `3.6.x` | :white_check_mark: |
| `< 2.2.x` | :x: | | `< 3.6.x` | :x: |
| `1.7.x` | :white_check_mark: | | `2.11.x` | :white_check_mark: |
| `< 1.7.x` | :x: | | `< 2.11.x` | :x: |
## Reporting a Vulnerability ## Reporting a Vulnerability
@@ -28,3 +23,5 @@ We want to keep Traefik safe for everyone.
If you've discovered a security vulnerability in Traefik, If you've discovered a security vulnerability in Traefik,
we appreciate your help in disclosing it to us in a responsible manner, we appreciate your help in disclosing it to us in a responsible manner,
by creating a [security advisory](https://github.com/traefik/traefik/security/advisories). by creating a [security advisory](https://github.com/traefik/traefik/security/advisories).
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).

View File

@@ -114,9 +114,7 @@ func runCmd(staticConfiguration *static.Configuration) error {
log.Debug().RawJSON("staticConfiguration", []byte(redactedStaticConfiguration)).Msg("Static configuration loaded [json]") log.Debug().RawJSON("staticConfiguration", []byte(redactedStaticConfiguration)).Msg("Static configuration loaded [json]")
} }
if staticConfiguration.Global.CheckNewVersion { checkNewVersion(staticConfiguration)
checkNewVersion()
}
stats(staticConfiguration) stats(staticConfiguration)
@@ -614,13 +612,28 @@ func setupTracing(ctx context.Context, conf *static.Tracing) (*tracing.Tracer, i
return tracer, closer return tracer, closer
} }
func checkNewVersion() { func checkNewVersion(staticConfiguration *static.Configuration) {
ticker := time.Tick(24 * time.Hour) logger := log.With().Logger()
safe.Go(func() {
for time.Sleep(10 * time.Minute); ; <-ticker { if staticConfiguration.Global.CheckNewVersion {
version.CheckNewVersion() logger.Info().Msg(`Version check is enabled.`)
} logger.Info().Msg(`Traefik checks for new releases to notify you if your version is out of date.`)
}) logger.Info().Msg(`It also collects usage data during this process.`)
logger.Info().Msg(`Check the documentation to get more info: https://doc.traefik.io/traefik/contributing/data-collection/`)
ticker := time.Tick(24 * time.Hour)
safe.Go(func() {
for time.Sleep(10 * time.Minute); ; <-ticker {
version.CheckNewVersion()
}
})
} else {
logger.Info().Msg(`
Version check is disabled.
You will not be notified if a new version is available.
More details: https://doc.traefik.io/traefik/contributing/data-collection/
`)
}
} }
func stats(staticConfiguration *static.Configuration) { func stats(staticConfiguration *static.Configuration) {

View File

@@ -1,17 +1,72 @@
--- ---
title: "Traefik Data Collection Documentation" title: "Traefik Data Collection Documentation"
description: "To learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances. Read the technical documentation." description: "Learn what data Traefik shares, how it is used, and how you can control it. This documentation explains both version check and anonymous usage statistics data. Read the technical documentation."
--- ---
# Data Collection # Data Collection
Understanding How Traefik is Being Used Understanding the data Traefik shares and how it is used
{: .subtitle } {: .subtitle }
## Configuration Example ## Introduction
Understanding how you use Traefik is very important to us: it helps us improve the solution in many different ways. Protecting user privacy is essential to Traefik Labs, and we design every data-sharing mechanism with transparency and minimalism in mind.
For this very reason, the sendAnonymousUsage option is mandatory: we want you to take time to consider whether or not you wish to share anonymous data with us, so we can benefit from your experience and use cases. This page describes the two types of data exchanged by Traefik and how to configure them.
For more details on how your data is handled, please refer to our [Privacy and Cookie Policy](https://traefik.io/legal/privacy-and-cookie-policy).
## Configuration Overview
Traefik provides two independent mechanisms:
- `checkNewVersion`, enabled by default. You may disable it at any time.
- `sendAnonymousUsage`, which requires explicit optin.
Examples below show how to activate or deactivate both of them.
```yaml tab="YAML"
global:
checkNewVersion: true # set to false to disable
sendAnonymousUsage: false # set to true to enable
```
```toml tab="TOML"
[global]
checkNewVersion = true # set to false to disable
sendAnonymousUsage = false # set to true to enable
```
```bash tab="CLI"
--global.checkNewVersion=true # set to false to disable
--global.sendAnonymousUsage=false # set to true to enable
```
A log message at startup clearly indicates whether each of those options are enabled or disabled.
## Version Check (`checkNewVersion`) Opt-out
Traefik periodically contacts `update.traefik.io` to determine whether a newer version is available.
When this request is made, Traefik shares the **running version** and the **public IP** of the instance.
The IP is used to build global usage statistics and does not influence the version comparison.
This mechanism helps you stay informed about updates and provides TraefikLabs with a broad view of which versions are deployed in the wild.
The collected IP addresses are also used for marketing purposes, specifically to detect companies running Traefik and offer them adapted support contracts, enterprise features, and tailored services.
If you want to explore the implementation, you can read the version check source code: [version.go](https://github.com/traefik/traefik/blob/master/pkg/version/version.go)
## Anonymous Usage Data (`sendAnonymousUsage`) Optin
Traefik can also collect anonymous usage statistics once per day, starting 10 minutes after it starts running.
These statistics include:
- the Traefik version,
- a hash of the configuration,
- an anonymized version of the static configuration (all sensitive fields removed: tokens, passwords, URLs, IP addresses, domains, emails, etc.).
This feature comes from this [public proposal](https://github.com/traefik/traefik/issues/2369).
This information helps TraefikLabs understand how Traefik is used in general and prioritize features and provider support accordingly. Dynamic configuration (routers and services) is never collected.
!!! example "Enabling Data Collection" !!! example "Enabling Data Collection"
@@ -32,21 +87,6 @@ For this very reason, the sendAnonymousUsage option is mandatory: we want you to
--global.sendAnonymousUsage --global.sendAnonymousUsage
``` ```
## Collected Data
This feature comes from this [public proposal](https://github.com/traefik/traefik/issues/2369).
In order to help us learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances.
Those data help us prioritize our developments and focus on what's important for our users (for example, which provider is popular, and which is not).
### What's collected / when ?
Once a day (the first call begins 10 minutes after the start of Traefik), we collect:
- the Traefik version number
- a hash of the configuration
- an **anonymized version** of the static configuration (token, username, password, URL, IP, domain, email, etc., are removed).
!!! info !!! info
- We do not collect the dynamic configuration information (routers & services). - We do not collect the dynamic configuration information (routers & services).
@@ -93,8 +133,9 @@ providers:
insecureSkipVerify: true insecureSkipVerify: true
``` ```
## The Code for Data Collection ### The Code for Anonymous Usage Collection
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go) If you want to explore the implementation, you can read the collector source code:
[collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go)
By default, we anonymize all configuration fields, except fields tagged with `export=true`. Traefik anonymizes all configuration fields by default, except those explicitly marked with `export=true`.

View File

@@ -525,3 +525,32 @@ To use the new `leasttime` load-balancer algorithm with the Kubernetes CRD provi
```shell ```shell
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
``` ```
## v3.6.2
### Ingress NGINX Provider
The KubernetesIngressNGINX Provider is no longer experimental in v3.6.2 and can be enabled without the `experimental.kubernetesIngressNGINX` option.
**Deprecated Configuration:**
??? example "Experimental kubernetesIngressNGINX option (deprecated)"
```yaml tab="File (YAML)"
experimental:
kubernetesIngressNGINX: true
```
```toml tab="File (TOML)"
[experimental]
kubernetesIngressNGINX=true
```
```bash tab="CLI"
--experimental.kubernetesIngressNGINX=true
```
**Migration Steps:**
1. Remove the `kubernetesIngressNGINX` option from the experimental section
2. Configure the provider using the [kubernetesIngressNGINX Provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md)

View File

@@ -21,7 +21,7 @@ For more details, check out the conformance [report](https://github.com/kubernet
!!! info "Helm Chart" !!! info "Helm Chart"
When using the Traefik [Helm Chart](../getting-started/install-traefik.md#use-the-helm-chart), the CRDs (Custom Resource Definitions) and RBAC (Role-Based Access Control) are automatically managed for you. When using the Traefik [Helm Chart](../getting-started/install-traefik.md#use-the-helm-chart), the CRDs (Custom Resource Definitions) and RBAC (Role-Based Access Control) are automatically managed for you.
The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L130). The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L323).
1. Install/update the Kubernetes Gateway API CRDs. 1. Install/update the Kubernetes Gateway API CRDs.

View File

@@ -17,7 +17,7 @@ For more details, check out the conformance [report](https://github.com/kubernet
!!! info "Using The Helm Chart" !!! info "Using The Helm Chart"
When using the Traefik [Helm Chart](../../../../getting-started/install-traefik.md#use-the-helm-chart), the CRDs (Custom Resource Definitions) and RBAC (Role-Based Access Control) are automatically managed for you. When using the Traefik [Helm Chart](../../../../getting-started/install-traefik.md#use-the-helm-chart), the CRDs (Custom Resource Definitions) and RBAC (Role-Based Access Control) are automatically managed for you.
The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L130). The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L323).
## Requirements ## Requirements

View File

@@ -3,67 +3,183 @@ title: "Traefik Kubernetes Ingress NGINX Documentation"
description: "Understand the requirements, routing configuration, and how to set up the Kubernetes Ingress NGINX provider. Read the technical documentation." description: "Understand the requirements, routing configuration, and how to set up the Kubernetes Ingress NGINX provider. Read the technical documentation."
--- ---
# Traefik & Ingresses with NGINX Annotations # Traefik & Ingresses with NGINX Annotations
The experimental Traefik Kubernetes Ingress NGINX provider is a Kubernetes Ingress controller; i.e, This provider is a Kubernetes Ingress controller that manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification.
it manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification. It also supports many of the [ingress-nginx](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) annotations on Ingresses, enabling teams to migrate from NGINX Ingress Controller to Traefik with minimal configuration changes.
It also supports some of the [ingress-nginx](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) annotations on ingresses to customize their behavior.
!!! warning "Ingress Discovery" !!! warning "NGINX Ingress Controller Retirement"
The Kubernetes Ingress NGINX provider is discovering by default all Ingresses in the cluster, The Kubernetes NGINX Ingress Controller project has announced its retirement in **March 2026** and will no longer receive updates or security patches.
which may lead to duplicated routers if you are also using the Kubernetes Ingress provider. Traefik provides a migration path by supporting NGINX annotations, allowing you to transition your workloads without rewriting all your Ingress configurations.
We recommend to use IngressClass for the Ingresses you want to be handled by this provider,
or to use the `watchNamespace` or `watchNamespaceSelector` options to limit the discovery of Ingresses to a specific namespace or set of namespaces. For more information about the NGINX Ingress Controller retirement, see the [official Kubernetes blog announcement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement).
## Ingress Discovery
This provider discovers all Ingresses in the cluster by default, which may lead to duplicated routers if you are also using the standard Kubernetes Ingress provider.
**Best Practices:**
- Use IngressClass to specify which Ingresses should be handled by this provider
- Configure `watchNamespace` to limit discovery to specific namespaces
- Use `watchNamespaceSelector` to target Ingresses based on namespace labels
## Configuration Example ## Configuration Example
As this provider is an experimental feature, it needs to be enabled in the experimental and in the provider sections of the configuration.
You can enable the Kubernetes Ingress NGINX provider as detailed below: You can enable the Kubernetes Ingress NGINX provider as detailed below:
```yaml tab="File (YAML)" ```yaml tab="File (YAML)"
experimental:
kubernetesIngressNGINX: true
providers: providers:
kubernetesIngressNGINX: {} kubernetesIngressNGINX:
endpoint: "https://kubernetes.default.svc"
token: "mytoken"
certAuthFilePath: "/path/to/ca.crt"
throttleDuration: "2s"
# Namespace discovery
watchNamespace: "default"
# OR use namespace selector (mutually exclusive with watchNamespace)
# watchNamespaceSelector: "environment=production"
# IngressClass configuration
ingressClass: "nginx"
controllerClass: "k8s.io/ingress-nginx"
watchIngressWithoutClass: false
ingressClassByName: false
# Status updates
publishService: "kube-system/traefik"
publishStatusAddress: "203.0.113.42"
# Default backend
defaultBackendService: "default/default-backend"
# Security
disableSvcExternalName: false
``` ```
```toml tab="File (TOML)" ```toml tab="File (TOML)"
[experimental.kubernetesIngressNGINX]
[providers.kubernetesIngressNGINX] [providers.kubernetesIngressNGINX]
endpoint = "https://kubernetes.default.svc"
token = "mytoken"
certAuthFilePath = "/path/to/ca.crt"
throttleDuration = "2s"
# Namespace discovery
watchNamespace = "default"
# OR use namespace selector (mutually exclusive with watchNamespace)
# watchNamespaceSelector = "environment=production"
# IngressClass configuration
ingressClass = "nginx"
controllerClass = "k8s.io/ingress-nginx"
watchIngressWithoutClass = false
ingressClassByName = false
# Status updates
publishService = "kube-system/traefik"
publishStatusAddress = "203.0.113.42"
# Default backend
defaultBackendService = "default/default-backend"
# Security
disableSvcExternalName = false
``` ```
```bash tab="CLI" ```bash tab="CLI"
--experimental.kubernetesingressnginx=true
--providers.kubernetesingressnginx=true --providers.kubernetesingressnginx=true
--providers.kubernetesingressnginx.endpoint=https://kubernetes.default.svc
--providers.kubernetesingressnginx.token=mytoken
--providers.kubernetesingressnginx.certauthfilepath=/path/to/ca.crt
--providers.kubernetesingressnginx.throttleduration=2s
--providers.kubernetesingressnginx.watchnamespace=default
--providers.kubernetesingressnginx.ingressclass=nginx
--providers.kubernetesingressnginx.controllerclass=k8s.io/ingress-nginx
--providers.kubernetesingressnginx.watchingresswithoutclass=false
--providers.kubernetesingressnginx.ingressclassbyname=false
--providers.kubernetesingressnginx.publishservice=kube-system/traefik
--providers.kubernetesingressnginx.publishstatusaddress=203.0.113.42
--providers.kubernetesingressnginx.defaultbackendservice=default/default-backend
--providers.kubernetesingressnginx.disablesvcexternalname=false
``` ```
The provider then watches for incoming ingresses events, such as the example below, ```yaml tab="Helm Chart Values"
and derives the corresponding dynamic configuration from it, providers:
which in turn creates the resulting routers, services, handlers, etc. kubernetesIngressNginx:
# -- Enable Kubernetes Ingress NGINX provider
enabled: true
# -- Kubernetes server endpoint (required for external cluster client)
endpoint: "https://kubernetes.default.svc"
# -- Kubernetes bearer token (not needed for in-cluster client)
token: "mytoken"
# -- Kubernetes certificate authority file path (not needed for in-cluster client)
certAuthFilePath: "/path/to/ca.crt"
# -- Ingress refresh throttle duration
throttleDuration: "2s"
# Namespace discovery
# -- Namespace the controller watches for updates to Kubernetes objects
# When using rbac.namespaced, it will watch helm release namespace and namespaces listed in this array
namespaces:
- default
# OR use namespace selector (mutually exclusive with namespaces)
# namespaceSelector: "environment=production"
# IngressClass configuration
# -- Name of the ingress class this controller satisfies
ingressClass: "nginx"
# -- Ingress Class Controller value this controller satisfies
controllerClass: "k8s.io/ingress-nginx"
# -- Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified
watchIngressWithoutClass: false
# -- Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class
ingressClassByName: false
# Status updates
# -- Service fronting the Ingress controller
publishService:
enabled: true
pathOverride: "kube-system/traefik"
# -- Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects
publishStatusAddress: "203.0.113.42"
# Default backend
# -- Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'
defaultBackendService: "default/default-backend"
# Security
# -- Disable support for Services of type ExternalName
disableSvcExternalName: false
```
This provider watches for incoming Ingress events and automatically translates NGINX annotations into Traefik's dynamic configuration, creating the corresponding routers, services, middlewares, and other components needed to route traffic to your cluster services.
## Configuration Options ## Configuration Options
<!-- markdownlint-disable MD013 --> <!-- markdownlint-disable MD013 -->
| Field | Description | Default | Required | | Field | Description | Default | Required |
|:------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------|:--------|:---------|
| <a id="opt-providers-providersThrottleDuration" href="#opt-providers-providersThrottleDuration" title="#opt-providers-providersThrottleDuration">`providers.providersThrottleDuration`</a> | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.<br />If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.<br />**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | | <a id="opt-providers-providers-ThrottleDuration" href="#opt-providers-providers-ThrottleDuration" title="#opt-providers-providers-ThrottleDuration">`providers.providers`<br/>`ThrottleDuration`</a> | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.<br />If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.<br />**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No |
| <a id="opt-providers-kubernetesIngressNGINX-endpoint" href="#opt-providers-kubernetesIngressNGINX-endpoint" title="#opt-providers-kubernetesIngressNGINX-endpoint">`providers.kubernetesIngressNGINX.endpoint`</a> | Server endpoint URL.<br />More information [here](#endpoint). | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-endpoint" href="#opt-providers-kubernetesIngressNGINX-endpoint" title="#opt-providers-kubernetesIngressNGINX-endpoint">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`endpoint`</a> | Server endpoint URL.<br />More information [here](#endpoint). | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-token" href="#opt-providers-kubernetesIngressNGINX-token" title="#opt-providers-kubernetesIngressNGINX-token">`providers.kubernetesIngressNGINX.token`</a> | Bearer token used for the Kubernetes client configuration. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-token" href="#opt-providers-kubernetesIngressNGINX-token" title="#opt-providers-kubernetesIngressNGINX-token">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`token`</a> | Bearer token used for the Kubernetes client configuration. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-certAuthFilePath" href="#opt-providers-kubernetesIngressNGINX-certAuthFilePath" title="#opt-providers-kubernetesIngressNGINX-certAuthFilePath">`providers.kubernetesIngressNGINX.certAuthFilePath`</a> | Path to the certificate authority file.<br />Used for the Kubernetes client configuration. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-certAuthFilePath" href="#opt-providers-kubernetesIngressNGINX-certAuthFilePath" title="#opt-providers-kubernetesIngressNGINX-certAuthFilePath">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`certAuthFilePath`</a> | Path to the certificate authority file.<br />Used for the Kubernetes client configuration. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-throttleDuration" href="#opt-providers-kubernetesIngressNGINX-throttleDuration" title="#opt-providers-kubernetesIngressNGINX-throttleDuration">`providers.kubernetesIngressNGINX.throttleDuration`</a> | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.<br />This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.<br />If empty, every event is caught. | 0s | No | | <a id="opt-providers-kubernetesIngressNGINX-throttleDuration" href="#opt-providers-kubernetesIngressNGINX-throttleDuration" title="#opt-providers-kubernetesIngressNGINX-throttleDuration">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`throttleDuration`</a> | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.<br />This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.<br />If empty, every event is caught. | 0s | No |
| <a id="opt-providers-kubernetesIngressNGINX-watchNamespace" href="#opt-providers-kubernetesIngressNGINX-watchNamespace" title="#opt-providers-kubernetesIngressNGINX-watchNamespace">`providers.kubernetesIngressNGINX.watchNamespace`</a> | Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-watchNamespace" href="#opt-providers-kubernetesIngressNGINX-watchNamespace" title="#opt-providers-kubernetesIngressNGINX-watchNamespace">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchNamespace`</a> | Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" href="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" title="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector">`providers.kubernetesIngressNGINX.watchNamespaceSelector`</a> | Selector selects namespaces the controller watches for updates to Kubernetes objects. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" href="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" title="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchNamespaceSelector`</a> | Selector selects namespaces the controller watches for updates to Kubernetes objects. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-ingressClass" href="#opt-providers-kubernetesIngressNGINX-ingressClass" title="#opt-providers-kubernetesIngressNGINX-ingressClass">`providers.kubernetesIngressNGINX.ingressClass`</a> | Name of the ingress class this controller satisfies. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-ingressClass" href="#opt-providers-kubernetesIngressNGINX-ingressClass" title="#opt-providers-kubernetesIngressNGINX-ingressClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`ingressClass`</a> | Name of the ingress class this controller satisfies. | "nginx" | No |
| <a id="opt-providers-kubernetesIngressNGINX-controllerClass" href="#opt-providers-kubernetesIngressNGINX-controllerClass" title="#opt-providers-kubernetesIngressNGINX-controllerClass">`providers.kubernetesIngressNGINX.controllerClass`</a> | Ingress Class Controller value this controller satisfies. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-controllerClass" href="#opt-providers-kubernetesIngressNGINX-controllerClass" title="#opt-providers-kubernetesIngressNGINX-controllerClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`controllerClass`</a> | Ingress Class Controller value this controller satisfies. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" href="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" title="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass">`providers.kubernetesIngressNGINX.watchIngressWithoutClass`</a> | Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | No | | <a id="opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" href="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" title="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchIngressWithoutClass`</a> | Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | No |
| <a id="opt-providers-kubernetesIngressNGINX-ingressClassByName" href="#opt-providers-kubernetesIngressNGINX-ingressClassByName" title="#opt-providers-kubernetesIngressNGINX-ingressClassByName">`providers.kubernetesIngressNGINX.ingressClassByName`</a> | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | No | | <a id="opt-providers-kubernetesIngressNGINX-ingressClassByName" href="#opt-providers-kubernetesIngressNGINX-ingressClassByName" title="#opt-providers-kubernetesIngressNGINX-ingressClassByName">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`ingressClassByName`</a> | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | No |
| <a id="opt-providers-kubernetesIngressNGINX-publishService" href="#opt-providers-kubernetesIngressNGINX-publishService" title="#opt-providers-kubernetesIngressNGINX-publishService">`providers.kubernetesIngressNGINX.publishService`</a> | Service fronting the Ingress controller. Takes the form namespace/name. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-publishService" href="#opt-providers-kubernetesIngressNGINX-publishService" title="#opt-providers-kubernetesIngressNGINX-publishService">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`publishService`</a> | Service fronting the Ingress controller. Takes the form `namespace/name`. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-publishStatusAddress" href="#opt-providers-kubernetesIngressNGINX-publishStatusAddress" title="#opt-providers-kubernetesIngressNGINX-publishStatusAddress">`providers.kubernetesIngressNGINX.publishStatusAddress`</a> | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-publishStatusAddress" href="#opt-providers-kubernetesIngressNGINX-publishStatusAddress" title="#opt-providers-kubernetesIngressNGINX-publishStatusAddress">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`publishStatusAddress`</a> | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-defaultBackendService" href="#opt-providers-kubernetesIngressNGINX-defaultBackendService" title="#opt-providers-kubernetesIngressNGINX-defaultBackendService">`providers.kubernetesIngressNGINX.defaultBackendService`</a> | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | "" | No | | <a id="opt-providers-kubernetesIngressNGINX-defaultBackendService" href="#opt-providers-kubernetesIngressNGINX-defaultBackendService" title="#opt-providers-kubernetesIngressNGINX-defaultBackendService">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`defaultBackendService`</a> | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | "" | No |
| <a id="opt-providers-kubernetesIngressNGINX-disableSvcExternalName" href="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName" title="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName">`providers.kubernetesIngressNGINX.disableSvcExternalName`</a> | Disable support for Services of type ExternalName. | false | No | | <a id="opt-providers-kubernetesIngressNGINX-disableSvcExternalName" href="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName" title="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`disableSvcExternalName`</a> | Disable support for Services of type ExternalName. | false | No |
<!-- markdownlint-enable MD013 --> <!-- markdownlint-enable MD013 -->

View File

@@ -5,21 +5,29 @@ description: "Understand the routing configuration for the Kubernetes Ingress NG
# Traefik & Ingresses with NGINX Annotations # Traefik & Ingresses with NGINX Annotations
The experimental Kubernetes Controller for Ingresses with NGINX annotations. Enable seamless migration from NGINX Ingress Controller to Traefik with NGINX annotation compatibility.
{: .subtitle } {: .subtitle }
!!! warning "Ingress Discovery" !!! warning "NGINX Ingress Controller Retirement"
The Kubernetes Ingress NGINX provider is discovering by default all Ingresses in the cluster, The Kubernetes NGINX Ingress Controller project has announced its retirement in **March 2026** and will no longer receive updates or security patches.
which may lead to duplicated routers if you are also using the Kubernetes Ingress provider. Traefik provides a migration path by supporting NGINX annotations, allowing you to transition your workloads without rewriting all your Ingress configurations.
We recommend to use IngressClass for the Ingresses you want to be handled by this provider,
or to use the `watchNamespace` or `watchNamespaceSelector` options to limit the discovery of Ingresses to a specific namespace or set of namespaces. For more information about the NGINX Ingress Controller retirement, see the [official Kubernetes blog announcement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement).
## Ingress Discovery
This provider discovers all Ingresses in the cluster by default, which may lead to duplicated routers if you are also using the standard Kubernetes Ingress provider.
**Best Practices:**
- Use IngressClass to specify which Ingresses should be handled by this provider
- Configure `watchNamespace` to limit discovery to specific namespaces
- Use `watchNamespaceSelector` to target Ingresses based on namespace labels
## Routing Configuration ## Routing Configuration
The Kubernetes Ingress NGINX provider watches for incoming ingresses events, such as the example below, This provider watches for incoming Ingress events and automatically translates NGINX annotations into Traefik's dynamic configuration, creating the corresponding routers, services, middlewares, and other components needed to handle your traffic.
and derives the corresponding dynamic configuration from it,
which in turn will create the resulting routers, services, handlers, etc.
## Configuration Example ## Configuration Example
@@ -239,32 +247,13 @@ which in turn will create the resulting routers, services, handlers, etc.
## Annotations Support ## Annotations Support
This section lists all known NGINX Ingress annotations, split between those currently implemented (with limitations if any) and those not implemented. This section lists all known NGINX Ingress annotations.
Limitations or behavioral differences are indicated where relevant. The following annotations are organized by category for easier navigation.
!!! warning "Global configuration" ### Authentication
Traefik does not expose all global configuration options to control default behaviors for ingresses.
Some behaviors that are globally configurable in NGINX (such as default SSL redirect, rate limiting, or affinity) are currently not supported and cannot be overridden per-ingress as in NGINX.
### Caveats and Key Behavioral Differences
- **Authentication**: Forward auth behaves differently and session caching is not supported. NGINX supports sub-request based auth, while Traefik forwards the original request.
- **Session Affinity**: Only persistent mode is supported.
- **Leader Election**: Not supported; no cluster mode with leader election.
- **Default Backend**: Only `defaultBackend` in Ingress spec is supported; the annotation is ignored.
- **Load Balancing**: Only round_robin is supported; EWMA and IP hash are not supported.
- **CORS**: NGINX responds with all configured headers unconditionally; Traefik handles headers differently between pre-flight and regular requests.
- **TLS/Backend Protocols**: AUTO_HTTP, FCGI and some TLS options are not supported in Traefik.
- **Path Handling**: Traefik preserves trailing slashes by default; NGINX removes them unless configured otherwise.
### Supported NGINX Annotations
| Annotation | Limitations / Notes | | Annotation | Limitations / Notes |
|-------------------------------------------------------|--------------------------------------------------------------------------------------------| |-------------------------------------------------------|--------------------------------------------------------------------------------------------|
| <a id="opt-nginx-ingress-kubernetes-ioaffinity" href="#opt-nginx-ingress-kubernetes-ioaffinity" title="#opt-nginx-ingress-kubernetes-ioaffinity">`nginx.ingress.kubernetes.io/affinity`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-mode" href="#opt-nginx-ingress-kubernetes-ioaffinity-mode" title="#opt-nginx-ingress-kubernetes-ioaffinity-mode">`nginx.ingress.kubernetes.io/affinity-mode`</a> | Only persistent mode supported; balanced/canary not supported. |
| <a id="opt-nginx-ingress-kubernetes-ioauth-type" href="#opt-nginx-ingress-kubernetes-ioauth-type" title="#opt-nginx-ingress-kubernetes-ioauth-type">`nginx.ingress.kubernetes.io/auth-type`</a> | | | <a id="opt-nginx-ingress-kubernetes-ioauth-type" href="#opt-nginx-ingress-kubernetes-ioauth-type" title="#opt-nginx-ingress-kubernetes-ioauth-type">`nginx.ingress.kubernetes.io/auth-type`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-secret" href="#opt-nginx-ingress-kubernetes-ioauth-secret" title="#opt-nginx-ingress-kubernetes-ioauth-secret">`nginx.ingress.kubernetes.io/auth-secret`</a> | | | <a id="opt-nginx-ingress-kubernetes-ioauth-secret" href="#opt-nginx-ingress-kubernetes-ioauth-secret" title="#opt-nginx-ingress-kubernetes-ioauth-secret">`nginx.ingress.kubernetes.io/auth-secret`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-secret-type" href="#opt-nginx-ingress-kubernetes-ioauth-secret-type" title="#opt-nginx-ingress-kubernetes-ioauth-secret-type">`nginx.ingress.kubernetes.io/auth-secret-type`</a> | | | <a id="opt-nginx-ingress-kubernetes-ioauth-secret-type" href="#opt-nginx-ingress-kubernetes-ioauth-secret-type" title="#opt-nginx-ingress-kubernetes-ioauth-secret-type">`nginx.ingress.kubernetes.io/auth-secret-type`</a> | |
@@ -272,29 +261,69 @@ Limitations or behavioral differences are indicated where relevant.
| <a id="opt-nginx-ingress-kubernetes-ioauth-url" href="#opt-nginx-ingress-kubernetes-ioauth-url" title="#opt-nginx-ingress-kubernetes-ioauth-url">`nginx.ingress.kubernetes.io/auth-url`</a> | Only URL and response headers copy supported. Forward auth behaves differently than NGINX. | | <a id="opt-nginx-ingress-kubernetes-ioauth-url" href="#opt-nginx-ingress-kubernetes-ioauth-url" title="#opt-nginx-ingress-kubernetes-ioauth-url">`nginx.ingress.kubernetes.io/auth-url`</a> | Only URL and response headers copy supported. Forward auth behaves differently than NGINX. |
| <a id="opt-nginx-ingress-kubernetes-ioauth-method" href="#opt-nginx-ingress-kubernetes-ioauth-method" title="#opt-nginx-ingress-kubernetes-ioauth-method">`nginx.ingress.kubernetes.io/auth-method`</a> | | | <a id="opt-nginx-ingress-kubernetes-ioauth-method" href="#opt-nginx-ingress-kubernetes-ioauth-method" title="#opt-nginx-ingress-kubernetes-ioauth-method">`nginx.ingress.kubernetes.io/auth-method`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-response-headers" href="#opt-nginx-ingress-kubernetes-ioauth-response-headers" title="#opt-nginx-ingress-kubernetes-ioauth-response-headers">`nginx.ingress.kubernetes.io/auth-response-headers`</a> | | | <a id="opt-nginx-ingress-kubernetes-ioauth-response-headers" href="#opt-nginx-ingress-kubernetes-ioauth-response-headers" title="#opt-nginx-ingress-kubernetes-ioauth-response-headers">`nginx.ingress.kubernetes.io/auth-response-headers`</a> | |
### SSL/TLS
| Annotation | Limitations / Notes |
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
| <a id="opt-nginx-ingress-kubernetes-iossl-redirect" href="#opt-nginx-ingress-kubernetes-iossl-redirect" title="#opt-nginx-ingress-kubernetes-iossl-redirect">`nginx.ingress.kubernetes.io/ssl-redirect`</a> | Cannot opt-out per route if enabled globally. | | <a id="opt-nginx-ingress-kubernetes-iossl-redirect" href="#opt-nginx-ingress-kubernetes-iossl-redirect" title="#opt-nginx-ingress-kubernetes-iossl-redirect">`nginx.ingress.kubernetes.io/ssl-redirect`</a> | Cannot opt-out per route if enabled globally. |
| <a id="opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" href="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" title="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect">`nginx.ingress.kubernetes.io/force-ssl-redirect`</a> | Cannot opt-out per route if enabled globally. | | <a id="opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" href="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" title="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect">`nginx.ingress.kubernetes.io/force-ssl-redirect`</a> | Cannot opt-out per route if enabled globally. |
| <a id="opt-nginx-ingress-kubernetes-iossl-passthrough" href="#opt-nginx-ingress-kubernetes-iossl-passthrough" title="#opt-nginx-ingress-kubernetes-iossl-passthrough">`nginx.ingress.kubernetes.io/ssl-passthrough`</a> | Some differences in SNI/default backend handling. | | <a id="opt-nginx-ingress-kubernetes-iossl-passthrough" href="#opt-nginx-ingress-kubernetes-iossl-passthrough" title="#opt-nginx-ingress-kubernetes-iossl-passthrough">`nginx.ingress.kubernetes.io/ssl-passthrough`</a> | Some differences in SNI/default backend handling. |
| <a id="opt-nginx-ingress-kubernetes-iouse-regex" href="#opt-nginx-ingress-kubernetes-iouse-regex" title="#opt-nginx-ingress-kubernetes-iouse-regex">`nginx.ingress.kubernetes.io/use-regex`</a> | | | <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name">`nginx.ingress.kubernetes.io/proxy-ssl-server-name`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name">`nginx.ingress.kubernetes.io/proxy-ssl-name`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify">`nginx.ingress.kubernetes.io/proxy-ssl-verify`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret">`nginx.ingress.kubernetes.io/proxy-ssl-secret`</a> | |
### Session Affinity
| Annotation | Limitations / Notes |
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
| <a id="opt-nginx-ingress-kubernetes-ioaffinity" href="#opt-nginx-ingress-kubernetes-ioaffinity" title="#opt-nginx-ingress-kubernetes-ioaffinity">`nginx.ingress.kubernetes.io/affinity`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-mode" href="#opt-nginx-ingress-kubernetes-ioaffinity-mode" title="#opt-nginx-ingress-kubernetes-ioaffinity-mode">`nginx.ingress.kubernetes.io/affinity-mode`</a> | Only persistent mode supported; balanced/canary not supported. |
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-name" href="#opt-nginx-ingress-kubernetes-iosession-cookie-name" title="#opt-nginx-ingress-kubernetes-iosession-cookie-name">`nginx.ingress.kubernetes.io/session-cookie-name`</a> | | | <a id="opt-nginx-ingress-kubernetes-iosession-cookie-name" href="#opt-nginx-ingress-kubernetes-iosession-cookie-name" title="#opt-nginx-ingress-kubernetes-iosession-cookie-name">`nginx.ingress.kubernetes.io/session-cookie-name`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-path" href="#opt-nginx-ingress-kubernetes-iosession-cookie-path" title="#opt-nginx-ingress-kubernetes-iosession-cookie-path">`nginx.ingress.kubernetes.io/session-cookie-path`</a> | | | <a id="opt-nginx-ingress-kubernetes-iosession-cookie-path" href="#opt-nginx-ingress-kubernetes-iosession-cookie-path" title="#opt-nginx-ingress-kubernetes-iosession-cookie-path">`nginx.ingress.kubernetes.io/session-cookie-path`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-domain" href="#opt-nginx-ingress-kubernetes-iosession-cookie-domain" title="#opt-nginx-ingress-kubernetes-iosession-cookie-domain">`nginx.ingress.kubernetes.io/session-cookie-domain`</a> | | | <a id="opt-nginx-ingress-kubernetes-iosession-cookie-domain" href="#opt-nginx-ingress-kubernetes-iosession-cookie-domain" title="#opt-nginx-ingress-kubernetes-iosession-cookie-domain">`nginx.ingress.kubernetes.io/session-cookie-domain`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-samesite" href="#opt-nginx-ingress-kubernetes-iosession-cookie-samesite" title="#opt-nginx-ingress-kubernetes-iosession-cookie-samesite">`nginx.ingress.kubernetes.io/session-cookie-samesite`</a> | | | <a id="opt-nginx-ingress-kubernetes-iosession-cookie-samesite" href="#opt-nginx-ingress-kubernetes-iosession-cookie-samesite" title="#opt-nginx-ingress-kubernetes-iosession-cookie-samesite">`nginx.ingress.kubernetes.io/session-cookie-samesite`</a> | |
### Load Balancing & Backend
| Annotation | Limitations / Notes |
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
| <a id="opt-nginx-ingress-kubernetes-ioload-balance" href="#opt-nginx-ingress-kubernetes-ioload-balance" title="#opt-nginx-ingress-kubernetes-ioload-balance">`nginx.ingress.kubernetes.io/load-balance`</a> | Only round_robin supported; ewma and IP hash not supported. | | <a id="opt-nginx-ingress-kubernetes-ioload-balance" href="#opt-nginx-ingress-kubernetes-ioload-balance" title="#opt-nginx-ingress-kubernetes-ioload-balance">`nginx.ingress.kubernetes.io/load-balance`</a> | Only round_robin supported; ewma and IP hash not supported. |
| <a id="opt-nginx-ingress-kubernetes-iobackend-protocol" href="#opt-nginx-ingress-kubernetes-iobackend-protocol" title="#opt-nginx-ingress-kubernetes-iobackend-protocol">`nginx.ingress.kubernetes.io/backend-protocol`</a> | FCGI and AUTO_HTTP not supported. | | <a id="opt-nginx-ingress-kubernetes-iobackend-protocol" href="#opt-nginx-ingress-kubernetes-iobackend-protocol" title="#opt-nginx-ingress-kubernetes-iobackend-protocol">`nginx.ingress.kubernetes.io/backend-protocol`</a> | FCGI and AUTO_HTTP not supported. |
| <a id="opt-nginx-ingress-kubernetes-ioservice-upstream" href="#opt-nginx-ingress-kubernetes-ioservice-upstream" title="#opt-nginx-ingress-kubernetes-ioservice-upstream">`nginx.ingress.kubernetes.io/service-upstream`</a> | |
### CORS
| Annotation | Limitations / Notes |
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
| <a id="opt-nginx-ingress-kubernetes-ioenable-cors" href="#opt-nginx-ingress-kubernetes-ioenable-cors" title="#opt-nginx-ingress-kubernetes-ioenable-cors">`nginx.ingress.kubernetes.io/enable-cors`</a> | Partial support. | | <a id="opt-nginx-ingress-kubernetes-ioenable-cors" href="#opt-nginx-ingress-kubernetes-ioenable-cors" title="#opt-nginx-ingress-kubernetes-ioenable-cors">`nginx.ingress.kubernetes.io/enable-cors`</a> | Partial support. |
| <a id="opt-nginx-ingress-kubernetes-iocors-allow-credentials" href="#opt-nginx-ingress-kubernetes-iocors-allow-credentials" title="#opt-nginx-ingress-kubernetes-iocors-allow-credentials">`nginx.ingress.kubernetes.io/cors-allow-credentials`</a> | | | <a id="opt-nginx-ingress-kubernetes-iocors-allow-credentials" href="#opt-nginx-ingress-kubernetes-iocors-allow-credentials" title="#opt-nginx-ingress-kubernetes-iocors-allow-credentials">`nginx.ingress.kubernetes.io/cors-allow-credentials`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocors-allow-headers" href="#opt-nginx-ingress-kubernetes-iocors-allow-headers" title="#opt-nginx-ingress-kubernetes-iocors-allow-headers">`nginx.ingress.kubernetes.io/cors-allow-headers`</a> | | | <a id="opt-nginx-ingress-kubernetes-iocors-allow-headers" href="#opt-nginx-ingress-kubernetes-iocors-allow-headers" title="#opt-nginx-ingress-kubernetes-iocors-allow-headers">`nginx.ingress.kubernetes.io/cors-allow-headers`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocors-allow-methods" href="#opt-nginx-ingress-kubernetes-iocors-allow-methods" title="#opt-nginx-ingress-kubernetes-iocors-allow-methods">`nginx.ingress.kubernetes.io/cors-allow-methods`</a> | | | <a id="opt-nginx-ingress-kubernetes-iocors-allow-methods" href="#opt-nginx-ingress-kubernetes-iocors-allow-methods" title="#opt-nginx-ingress-kubernetes-iocors-allow-methods">`nginx.ingress.kubernetes.io/cors-allow-methods`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocors-allow-origin" href="#opt-nginx-ingress-kubernetes-iocors-allow-origin" title="#opt-nginx-ingress-kubernetes-iocors-allow-origin">`nginx.ingress.kubernetes.io/cors-allow-origin`</a> | | | <a id="opt-nginx-ingress-kubernetes-iocors-allow-origin" href="#opt-nginx-ingress-kubernetes-iocors-allow-origin" title="#opt-nginx-ingress-kubernetes-iocors-allow-origin">`nginx.ingress.kubernetes.io/cors-allow-origin`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocors-max-age" href="#opt-nginx-ingress-kubernetes-iocors-max-age" title="#opt-nginx-ingress-kubernetes-iocors-max-age">`nginx.ingress.kubernetes.io/cors-max-age`</a> | | | <a id="opt-nginx-ingress-kubernetes-iocors-max-age" href="#opt-nginx-ingress-kubernetes-iocors-max-age" title="#opt-nginx-ingress-kubernetes-iocors-max-age">`nginx.ingress.kubernetes.io/cors-max-age`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name">`nginx.ingress.kubernetes.io/proxy-ssl-server-name`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name">`nginx.ingress.kubernetes.io/proxy-ssl-name`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify">`nginx.ingress.kubernetes.io/proxy-ssl-verify`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret">`nginx.ingress.kubernetes.io/proxy-ssl-secret`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioservice-upstream" href="#opt-nginx-ingress-kubernetes-ioservice-upstream" title="#opt-nginx-ingress-kubernetes-ioservice-upstream">`nginx.ingress.kubernetes.io/service-upstream`</a> | |
**Unsupported NGINX Annotations** ### Routing
| Annotation | Limitations / Notes |
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
| <a id="opt-nginx-ingress-kubernetes-iouse-regex" href="#opt-nginx-ingress-kubernetes-iouse-regex" title="#opt-nginx-ingress-kubernetes-iouse-regex">`nginx.ingress.kubernetes.io/use-regex`</a> | |
## Limitations
### Caveats and Key Behavioral Differences
- **Authentication**: Forward auth behaves differently and session caching is not supported. NGINX supports sub-request based auth, while Traefik forwards the original request.
- **Session Affinity**: Only persistent mode is supported.
- **Leader Election**: Not supported; no cluster mode with leader election.
- **Default Backend**: Only defaultBackend in Ingress spec is supported; the annotation is ignored.
- **Load Balancing**: Only round_robin is supported; EWMA and IP hash are not supported.
- **CORS**: NGINX responds with all configured headers unconditionally; Traefik handles headers differently between pre-flight and regular requests.
- **TLS/Backend Protocols**: AUTO_HTTP, FCGI and some TLS options are not supported in Traefik.
- **Path Handling**: Traefik preserves trailing slashes by default; NGINX removes them unless configured otherwise
### Unsupported Annotations
!!! question "Want to Add Support for More Annotations?" !!! question "Want to Add Support for More Annotations?"
@@ -305,98 +334,103 @@ Limitations or behavioral differences are indicated where relevant.
All contributions and suggestions are welcome — let's build this together! All contributions and suggestions are welcome — let's build this together!
| Annotation | Notes | | Annotation | Notes |
|-----------------------------------------------------------------------------|------------------------------------------------------| |-----------------------------------------------------------------------------|------------------------------------------------------|
| <a id="opt-nginx-ingress-kubernetes-ioapp-root" href="#opt-nginx-ingress-kubernetes-ioapp-root" title="#opt-nginx-ingress-kubernetes-ioapp-root">`nginx.ingress.kubernetes.io/app-root`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioapp-root" href="#opt-nginx-ingress-kubernetes-ioapp-root" title="#opt-nginx-ingress-kubernetes-ioapp-root">`nginx.ingress.kubernetes.io/app-root`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" href="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" title="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior">`nginx.ingress.kubernetes.io/affinity-canary-behavior`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" href="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" title="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior">`nginx.ingress.kubernetes.io/affinity-canary-behavior`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-secret" href="#opt-nginx-ingress-kubernetes-ioauth-tls-secret" title="#opt-nginx-ingress-kubernetes-ioauth-tls-secret">`nginx.ingress.kubernetes.io/auth-tls-secret`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-tls-secret" href="#opt-nginx-ingress-kubernetes-ioauth-tls-secret" title="#opt-nginx-ingress-kubernetes-ioauth-tls-secret">`nginx.ingress.kubernetes.io/auth-tls-secret`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth">`nginx.ingress.kubernetes.io/auth-tls-verify-depth`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth">`nginx.ingress.kubernetes.io/auth-tls-verify-depth`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client">`nginx.ingress.kubernetes.io/auth-tls-verify-client`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client">`nginx.ingress.kubernetes.io/auth-tls-verify-client`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-error-page" href="#opt-nginx-ingress-kubernetes-ioauth-tls-error-page" title="#opt-nginx-ingress-kubernetes-ioauth-tls-error-page">`nginx.ingress.kubernetes.io/auth-tls-error-page`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-tls-error-page" href="#opt-nginx-ingress-kubernetes-ioauth-tls-error-page" title="#opt-nginx-ingress-kubernetes-ioauth-tls-error-page">`nginx.ingress.kubernetes.io/auth-tls-error-page`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream" href="#opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream" title="#opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream">`nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream" href="#opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream" title="#opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream">`nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-match-cn" href="#opt-nginx-ingress-kubernetes-ioauth-tls-match-cn" title="#opt-nginx-ingress-kubernetes-ioauth-tls-match-cn">`nginx.ingress.kubernetes.io/auth-tls-match-cn`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-tls-match-cn" href="#opt-nginx-ingress-kubernetes-ioauth-tls-match-cn" title="#opt-nginx-ingress-kubernetes-ioauth-tls-match-cn">`nginx.ingress.kubernetes.io/auth-tls-match-cn`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-cache-key" href="#opt-nginx-ingress-kubernetes-ioauth-cache-key" title="#opt-nginx-ingress-kubernetes-ioauth-cache-key">`nginx.ingress.kubernetes.io/auth-cache-key`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-cache-key" href="#opt-nginx-ingress-kubernetes-ioauth-cache-key" title="#opt-nginx-ingress-kubernetes-ioauth-cache-key">`nginx.ingress.kubernetes.io/auth-cache-key`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-cache-duration" href="#opt-nginx-ingress-kubernetes-ioauth-cache-duration" title="#opt-nginx-ingress-kubernetes-ioauth-cache-duration">`nginx.ingress.kubernetes.io/auth-cache-duration`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-cache-duration" href="#opt-nginx-ingress-kubernetes-ioauth-cache-duration" title="#opt-nginx-ingress-kubernetes-ioauth-cache-duration">`nginx.ingress.kubernetes.io/auth-cache-duration`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive">`nginx.ingress.kubernetes.io/auth-keepalive`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive">`nginx.ingress.kubernetes.io/auth-keepalive`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive-share-vars" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive-share-vars" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive-share-vars">`nginx.ingress.kubernetes.io/auth-keepalive-share-vars`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive-share-vars" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive-share-vars" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive-share-vars">`nginx.ingress.kubernetes.io/auth-keepalive-share-vars`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive-requests" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive-requests" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive-requests">`nginx.ingress.kubernetes.io/auth-keepalive-requests`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive-requests" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive-requests" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive-requests">`nginx.ingress.kubernetes.io/auth-keepalive-requests`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive-timeout" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive-timeout" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive-timeout">`nginx.ingress.kubernetes.io/auth-keepalive-timeout`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-keepalive-timeout" href="#opt-nginx-ingress-kubernetes-ioauth-keepalive-timeout" title="#opt-nginx-ingress-kubernetes-ioauth-keepalive-timeout">`nginx.ingress.kubernetes.io/auth-keepalive-timeout`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-proxy-set-headers" href="#opt-nginx-ingress-kubernetes-ioauth-proxy-set-headers" title="#opt-nginx-ingress-kubernetes-ioauth-proxy-set-headers">`nginx.ingress.kubernetes.io/auth-proxy-set-headers`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-proxy-set-headers" href="#opt-nginx-ingress-kubernetes-ioauth-proxy-set-headers" title="#opt-nginx-ingress-kubernetes-ioauth-proxy-set-headers">`nginx.ingress.kubernetes.io/auth-proxy-set-headers`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioauth-snippet" href="#opt-nginx-ingress-kubernetes-ioauth-snippet" title="#opt-nginx-ingress-kubernetes-ioauth-snippet">`nginx.ingress.kubernetes.io/auth-snippet`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioauth-snippet" href="#opt-nginx-ingress-kubernetes-ioauth-snippet" title="#opt-nginx-ingress-kubernetes-ioauth-snippet">`nginx.ingress.kubernetes.io/auth-snippet`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioenable-global-auth" href="#opt-nginx-ingress-kubernetes-ioenable-global-auth" title="#opt-nginx-ingress-kubernetes-ioenable-global-auth">`nginx.ingress.kubernetes.io/enable-global-auth`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioenable-global-auth" href="#opt-nginx-ingress-kubernetes-ioenable-global-auth" title="#opt-nginx-ingress-kubernetes-ioenable-global-auth">`nginx.ingress.kubernetes.io/enable-global-auth`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocanary" href="#opt-nginx-ingress-kubernetes-iocanary" title="#opt-nginx-ingress-kubernetes-iocanary">`nginx.ingress.kubernetes.io/canary`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocanary" href="#opt-nginx-ingress-kubernetes-iocanary" title="#opt-nginx-ingress-kubernetes-iocanary">`nginx.ingress.kubernetes.io/canary`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocanary-by-header" href="#opt-nginx-ingress-kubernetes-iocanary-by-header" title="#opt-nginx-ingress-kubernetes-iocanary-by-header">`nginx.ingress.kubernetes.io/canary-by-header`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocanary-by-header" href="#opt-nginx-ingress-kubernetes-iocanary-by-header" title="#opt-nginx-ingress-kubernetes-iocanary-by-header">`nginx.ingress.kubernetes.io/canary-by-header`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocanary-by-header-value" href="#opt-nginx-ingress-kubernetes-iocanary-by-header-value" title="#opt-nginx-ingress-kubernetes-iocanary-by-header-value">`nginx.ingress.kubernetes.io/canary-by-header-value`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocanary-by-header-value" href="#opt-nginx-ingress-kubernetes-iocanary-by-header-value" title="#opt-nginx-ingress-kubernetes-iocanary-by-header-value">`nginx.ingress.kubernetes.io/canary-by-header-value`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocanary-by-header-pattern" href="#opt-nginx-ingress-kubernetes-iocanary-by-header-pattern" title="#opt-nginx-ingress-kubernetes-iocanary-by-header-pattern">`nginx.ingress.kubernetes.io/canary-by-header-pattern`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocanary-by-header-pattern" href="#opt-nginx-ingress-kubernetes-iocanary-by-header-pattern" title="#opt-nginx-ingress-kubernetes-iocanary-by-header-pattern">`nginx.ingress.kubernetes.io/canary-by-header-pattern`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocanary-by-cookie" href="#opt-nginx-ingress-kubernetes-iocanary-by-cookie" title="#opt-nginx-ingress-kubernetes-iocanary-by-cookie">`nginx.ingress.kubernetes.io/canary-by-cookie`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocanary-by-cookie" href="#opt-nginx-ingress-kubernetes-iocanary-by-cookie" title="#opt-nginx-ingress-kubernetes-iocanary-by-cookie">`nginx.ingress.kubernetes.io/canary-by-cookie`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocanary-weight" href="#opt-nginx-ingress-kubernetes-iocanary-weight" title="#opt-nginx-ingress-kubernetes-iocanary-weight">`nginx.ingress.kubernetes.io/canary-weight`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocanary-weight" href="#opt-nginx-ingress-kubernetes-iocanary-weight" title="#opt-nginx-ingress-kubernetes-iocanary-weight">`nginx.ingress.kubernetes.io/canary-weight`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocanary-weight-total" href="#opt-nginx-ingress-kubernetes-iocanary-weight-total" title="#opt-nginx-ingress-kubernetes-iocanary-weight-total">`nginx.ingress.kubernetes.io/canary-weight-total`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocanary-weight-total" href="#opt-nginx-ingress-kubernetes-iocanary-weight-total" title="#opt-nginx-ingress-kubernetes-iocanary-weight-total">`nginx.ingress.kubernetes.io/canary-weight-total`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioclient-body-buffer-size" href="#opt-nginx-ingress-kubernetes-ioclient-body-buffer-size" title="#opt-nginx-ingress-kubernetes-ioclient-body-buffer-size">`nginx.ingress.kubernetes.io/client-body-buffer-size`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioclient-body-buffer-size" href="#opt-nginx-ingress-kubernetes-ioclient-body-buffer-size" title="#opt-nginx-ingress-kubernetes-ioclient-body-buffer-size">`nginx.ingress.kubernetes.io/client-body-buffer-size`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioconfiguration-snippet" href="#opt-nginx-ingress-kubernetes-ioconfiguration-snippet" title="#opt-nginx-ingress-kubernetes-ioconfiguration-snippet">`nginx.ingress.kubernetes.io/configuration-snippet`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioconfiguration-snippet" href="#opt-nginx-ingress-kubernetes-ioconfiguration-snippet" title="#opt-nginx-ingress-kubernetes-ioconfiguration-snippet">`nginx.ingress.kubernetes.io/configuration-snippet`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iocustom-http-errors" href="#opt-nginx-ingress-kubernetes-iocustom-http-errors" title="#opt-nginx-ingress-kubernetes-iocustom-http-errors">`nginx.ingress.kubernetes.io/custom-http-errors`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iocustom-http-errors" href="#opt-nginx-ingress-kubernetes-iocustom-http-errors" title="#opt-nginx-ingress-kubernetes-iocustom-http-errors">`nginx.ingress.kubernetes.io/custom-http-errors`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iodisable-proxy-intercept-errors" href="#opt-nginx-ingress-kubernetes-iodisable-proxy-intercept-errors" title="#opt-nginx-ingress-kubernetes-iodisable-proxy-intercept-errors">`nginx.ingress.kubernetes.io/disable-proxy-intercept-errors`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iodisable-proxy-intercept-errors" href="#opt-nginx-ingress-kubernetes-iodisable-proxy-intercept-errors" title="#opt-nginx-ingress-kubernetes-iodisable-proxy-intercept-errors">`nginx.ingress.kubernetes.io/disable-proxy-intercept-errors`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iodefault-backend" href="#opt-nginx-ingress-kubernetes-iodefault-backend" title="#opt-nginx-ingress-kubernetes-iodefault-backend">`nginx.ingress.kubernetes.io/default-backend`</a> | Not supported yet; use `defaultBackend` in Ingress spec. | | <a id="opt-nginx-ingress-kubernetes-iodefault-backend" href="#opt-nginx-ingress-kubernetes-iodefault-backend" title="#opt-nginx-ingress-kubernetes-iodefault-backend">`nginx.ingress.kubernetes.io/default-backend`</a> | Use `defaultBackend` in Ingress spec. |
| <a id="opt-nginx-ingress-kubernetes-iolimit-rate-after" href="#opt-nginx-ingress-kubernetes-iolimit-rate-after" title="#opt-nginx-ingress-kubernetes-iolimit-rate-after">`nginx.ingress.kubernetes.io/limit-rate-after`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iolimit-rate-after" href="#opt-nginx-ingress-kubernetes-iolimit-rate-after" title="#opt-nginx-ingress-kubernetes-iolimit-rate-after">`nginx.ingress.kubernetes.io/limit-rate-after`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iolimit-rate" href="#opt-nginx-ingress-kubernetes-iolimit-rate" title="#opt-nginx-ingress-kubernetes-iolimit-rate">`nginx.ingress.kubernetes.io/limit-rate`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iolimit-rate" href="#opt-nginx-ingress-kubernetes-iolimit-rate" title="#opt-nginx-ingress-kubernetes-iolimit-rate">`nginx.ingress.kubernetes.io/limit-rate`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iolimit-whitelist" href="#opt-nginx-ingress-kubernetes-iolimit-whitelist" title="#opt-nginx-ingress-kubernetes-iolimit-whitelist">`nginx.ingress.kubernetes.io/limit-whitelist`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iolimit-whitelist" href="#opt-nginx-ingress-kubernetes-iolimit-whitelist" title="#opt-nginx-ingress-kubernetes-iolimit-whitelist">`nginx.ingress.kubernetes.io/limit-whitelist`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iolimit-rps" href="#opt-nginx-ingress-kubernetes-iolimit-rps" title="#opt-nginx-ingress-kubernetes-iolimit-rps">`nginx.ingress.kubernetes.io/limit-rps`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iolimit-rps" href="#opt-nginx-ingress-kubernetes-iolimit-rps" title="#opt-nginx-ingress-kubernetes-iolimit-rps">`nginx.ingress.kubernetes.io/limit-rps`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iolimit-rpm" href="#opt-nginx-ingress-kubernetes-iolimit-rpm" title="#opt-nginx-ingress-kubernetes-iolimit-rpm">`nginx.ingress.kubernetes.io/limit-rpm`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iolimit-rpm" href="#opt-nginx-ingress-kubernetes-iolimit-rpm" title="#opt-nginx-ingress-kubernetes-iolimit-rpm">`nginx.ingress.kubernetes.io/limit-rpm`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iolimit-burst-multiplier" href="#opt-nginx-ingress-kubernetes-iolimit-burst-multiplier" title="#opt-nginx-ingress-kubernetes-iolimit-burst-multiplier">`nginx.ingress.kubernetes.io/limit-burst-multiplier`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iolimit-burst-multiplier" href="#opt-nginx-ingress-kubernetes-iolimit-burst-multiplier" title="#opt-nginx-ingress-kubernetes-iolimit-burst-multiplier">`nginx.ingress.kubernetes.io/limit-burst-multiplier`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iolimit-connections" href="#opt-nginx-ingress-kubernetes-iolimit-connections" title="#opt-nginx-ingress-kubernetes-iolimit-connections">`nginx.ingress.kubernetes.io/limit-connections`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iolimit-connections" href="#opt-nginx-ingress-kubernetes-iolimit-connections" title="#opt-nginx-ingress-kubernetes-iolimit-connections">`nginx.ingress.kubernetes.io/limit-connections`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit">`nginx.ingress.kubernetes.io/global-rate-limit`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit">`nginx.ingress.kubernetes.io/global-rate-limit`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit-window" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-window" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-window">`nginx.ingress.kubernetes.io/global-rate-limit-window`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit-window" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-window" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-window">`nginx.ingress.kubernetes.io/global-rate-limit-window`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit-key" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-key" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-key">`nginx.ingress.kubernetes.io/global-rate-limit-key`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit-key" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-key" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-key">`nginx.ingress.kubernetes.io/global-rate-limit-key`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs">`nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs">`nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iopermanent-redirect" href="#opt-nginx-ingress-kubernetes-iopermanent-redirect" title="#opt-nginx-ingress-kubernetes-iopermanent-redirect">`nginx.ingress.kubernetes.io/permanent-redirect`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iopermanent-redirect" href="#opt-nginx-ingress-kubernetes-iopermanent-redirect" title="#opt-nginx-ingress-kubernetes-iopermanent-redirect">`nginx.ingress.kubernetes.io/permanent-redirect`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iopermanent-redirect-code" href="#opt-nginx-ingress-kubernetes-iopermanent-redirect-code" title="#opt-nginx-ingress-kubernetes-iopermanent-redirect-code">`nginx.ingress.kubernetes.io/permanent-redirect-code`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iopermanent-redirect-code" href="#opt-nginx-ingress-kubernetes-iopermanent-redirect-code" title="#opt-nginx-ingress-kubernetes-iopermanent-redirect-code">`nginx.ingress.kubernetes.io/permanent-redirect-code`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iotemporal-redirect" href="#opt-nginx-ingress-kubernetes-iotemporal-redirect" title="#opt-nginx-ingress-kubernetes-iotemporal-redirect">`nginx.ingress.kubernetes.io/temporal-redirect`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iotemporal-redirect" href="#opt-nginx-ingress-kubernetes-iotemporal-redirect" title="#opt-nginx-ingress-kubernetes-iotemporal-redirect">`nginx.ingress.kubernetes.io/temporal-redirect`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iopreserve-trailing-slash" href="#opt-nginx-ingress-kubernetes-iopreserve-trailing-slash" title="#opt-nginx-ingress-kubernetes-iopreserve-trailing-slash">`nginx.ingress.kubernetes.io/preserve-trailing-slash`</a> | Not supported yet; Traefik preserves by default. | | <a id="opt-nginx-ingress-kubernetes-iopreserve-trailing-slash" href="#opt-nginx-ingress-kubernetes-iopreserve-trailing-slash" title="#opt-nginx-ingress-kubernetes-iopreserve-trailing-slash">`nginx.ingress.kubernetes.io/preserve-trailing-slash`</a> | Traefik preserves trailing slash by default. |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-cookie-domain" href="#opt-nginx-ingress-kubernetes-ioproxy-cookie-domain" title="#opt-nginx-ingress-kubernetes-ioproxy-cookie-domain">`nginx.ingress.kubernetes.io/proxy-cookie-domain`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-cookie-domain" href="#opt-nginx-ingress-kubernetes-ioproxy-cookie-domain" title="#opt-nginx-ingress-kubernetes-ioproxy-cookie-domain">`nginx.ingress.kubernetes.io/proxy-cookie-domain`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-cookie-path" href="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path" title="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path">`nginx.ingress.kubernetes.io/proxy-cookie-path`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-cookie-path" href="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path" title="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path">`nginx.ingress.kubernetes.io/proxy-cookie-path`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout">`nginx.ingress.kubernetes.io/proxy-connect-timeout`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout">`nginx.ingress.kubernetes.io/proxy-connect-timeout`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-send-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-send-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-send-timeout">`nginx.ingress.kubernetes.io/proxy-send-timeout`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-send-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-send-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-send-timeout">`nginx.ingress.kubernetes.io/proxy-send-timeout`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-read-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-read-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-read-timeout">`nginx.ingress.kubernetes.io/proxy-read-timeout`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-read-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-read-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-read-timeout">`nginx.ingress.kubernetes.io/proxy-read-timeout`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-next-upstream" href="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream" title="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream">`nginx.ingress.kubernetes.io/proxy-next-upstream`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-next-upstream" href="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream" title="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream">`nginx.ingress.kubernetes.io/proxy-next-upstream`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-next-upstream-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-timeout">`nginx.ingress.kubernetes.io/proxy-next-upstream-timeout`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-next-upstream-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-timeout">`nginx.ingress.kubernetes.io/proxy-next-upstream-timeout`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-next-upstream-tries" href="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-tries" title="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-tries">`nginx.ingress.kubernetes.io/proxy-next-upstream-tries`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-next-upstream-tries" href="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-tries" title="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream-tries">`nginx.ingress.kubernetes.io/proxy-next-upstream-tries`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-request-buffering" href="#opt-nginx-ingress-kubernetes-ioproxy-request-buffering" title="#opt-nginx-ingress-kubernetes-ioproxy-request-buffering">`nginx.ingress.kubernetes.io/proxy-request-buffering`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-request-buffering" href="#opt-nginx-ingress-kubernetes-ioproxy-request-buffering" title="#opt-nginx-ingress-kubernetes-ioproxy-request-buffering">`nginx.ingress.kubernetes.io/proxy-request-buffering`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-redirect-from" href="#opt-nginx-ingress-kubernetes-ioproxy-redirect-from" title="#opt-nginx-ingress-kubernetes-ioproxy-redirect-from">`nginx.ingress.kubernetes.io/proxy-redirect-from`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-redirect-from" href="#opt-nginx-ingress-kubernetes-ioproxy-redirect-from" title="#opt-nginx-ingress-kubernetes-ioproxy-redirect-from">`nginx.ingress.kubernetes.io/proxy-redirect-from`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-redirect-to" href="#opt-nginx-ingress-kubernetes-ioproxy-redirect-to" title="#opt-nginx-ingress-kubernetes-ioproxy-redirect-to">`nginx.ingress.kubernetes.io/proxy-redirect-to`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-redirect-to" href="#opt-nginx-ingress-kubernetes-ioproxy-redirect-to" title="#opt-nginx-ingress-kubernetes-ioproxy-redirect-to">`nginx.ingress.kubernetes.io/proxy-redirect-to`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-http-version" href="#opt-nginx-ingress-kubernetes-ioproxy-http-version" title="#opt-nginx-ingress-kubernetes-ioproxy-http-version">`nginx.ingress.kubernetes.io/proxy-http-version`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-http-version" href="#opt-nginx-ingress-kubernetes-ioproxy-http-version" title="#opt-nginx-ingress-kubernetes-ioproxy-http-version">`nginx.ingress.kubernetes.io/proxy-http-version`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-ciphers" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-ciphers" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-ciphers">`nginx.ingress.kubernetes.io/proxy-ssl-ciphers`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-ciphers" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-ciphers" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-ciphers">`nginx.ingress.kubernetes.io/proxy-ssl-ciphers`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-verify-depth" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify-depth" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify-depth">`nginx.ingress.kubernetes.io/proxy-ssl-verify-depth`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-verify-depth" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify-depth" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify-depth">`nginx.ingress.kubernetes.io/proxy-ssl-verify-depth`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-protocols" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-protocols" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-protocols">`nginx.ingress.kubernetes.io/proxy-ssl-protocols`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-protocols" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-protocols" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-protocols">`nginx.ingress.kubernetes.io/proxy-ssl-protocols`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioenable-rewrite-log" href="#opt-nginx-ingress-kubernetes-ioenable-rewrite-log" title="#opt-nginx-ingress-kubernetes-ioenable-rewrite-log">`nginx.ingress.kubernetes.io/enable-rewrite-log`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioenable-rewrite-log" href="#opt-nginx-ingress-kubernetes-ioenable-rewrite-log" title="#opt-nginx-ingress-kubernetes-ioenable-rewrite-log">`nginx.ingress.kubernetes.io/enable-rewrite-log`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iorewrite-target" href="#opt-nginx-ingress-kubernetes-iorewrite-target" title="#opt-nginx-ingress-kubernetes-iorewrite-target">`nginx.ingress.kubernetes.io/rewrite-target`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iorewrite-target" href="#opt-nginx-ingress-kubernetes-iorewrite-target" title="#opt-nginx-ingress-kubernetes-iorewrite-target">`nginx.ingress.kubernetes.io/rewrite-target`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iosatisfy" href="#opt-nginx-ingress-kubernetes-iosatisfy" title="#opt-nginx-ingress-kubernetes-iosatisfy">`nginx.ingress.kubernetes.io/satisfy`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iosatisfy" href="#opt-nginx-ingress-kubernetes-iosatisfy" title="#opt-nginx-ingress-kubernetes-iosatisfy">`nginx.ingress.kubernetes.io/satisfy`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioserver-alias" href="#opt-nginx-ingress-kubernetes-ioserver-alias" title="#opt-nginx-ingress-kubernetes-ioserver-alias">`nginx.ingress.kubernetes.io/server-alias`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioserver-alias" href="#opt-nginx-ingress-kubernetes-ioserver-alias" title="#opt-nginx-ingress-kubernetes-ioserver-alias">`nginx.ingress.kubernetes.io/server-alias`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioserver-snippet" href="#opt-nginx-ingress-kubernetes-ioserver-snippet" title="#opt-nginx-ingress-kubernetes-ioserver-snippet">`nginx.ingress.kubernetes.io/server-snippet`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioserver-snippet" href="#opt-nginx-ingress-kubernetes-ioserver-snippet" title="#opt-nginx-ingress-kubernetes-ioserver-snippet">`nginx.ingress.kubernetes.io/server-snippet`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-conditional-samesite-none" href="#opt-nginx-ingress-kubernetes-iosession-cookie-conditional-samesite-none" title="#opt-nginx-ingress-kubernetes-iosession-cookie-conditional-samesite-none">`nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iosession-cookie-conditional-samesite-none" href="#opt-nginx-ingress-kubernetes-iosession-cookie-conditional-samesite-none" title="#opt-nginx-ingress-kubernetes-iosession-cookie-conditional-samesite-none">`nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-expires" href="#opt-nginx-ingress-kubernetes-iosession-cookie-expires" title="#opt-nginx-ingress-kubernetes-iosession-cookie-expires">`nginx.ingress.kubernetes.io/session-cookie-expires`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iosession-cookie-expires" href="#opt-nginx-ingress-kubernetes-iosession-cookie-expires" title="#opt-nginx-ingress-kubernetes-iosession-cookie-expires">`nginx.ingress.kubernetes.io/session-cookie-expires`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-change-on-failure" href="#opt-nginx-ingress-kubernetes-iosession-cookie-change-on-failure" title="#opt-nginx-ingress-kubernetes-iosession-cookie-change-on-failure">`nginx.ingress.kubernetes.io/session-cookie-change-on-failure`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iosession-cookie-change-on-failure" href="#opt-nginx-ingress-kubernetes-iosession-cookie-change-on-failure" title="#opt-nginx-ingress-kubernetes-iosession-cookie-change-on-failure">`nginx.ingress.kubernetes.io/session-cookie-change-on-failure`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iossl-ciphers" href="#opt-nginx-ingress-kubernetes-iossl-ciphers" title="#opt-nginx-ingress-kubernetes-iossl-ciphers">`nginx.ingress.kubernetes.io/ssl-ciphers`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iossl-ciphers" href="#opt-nginx-ingress-kubernetes-iossl-ciphers" title="#opt-nginx-ingress-kubernetes-iossl-ciphers">`nginx.ingress.kubernetes.io/ssl-ciphers`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iossl-prefer-server-ciphers" href="#opt-nginx-ingress-kubernetes-iossl-prefer-server-ciphers" title="#opt-nginx-ingress-kubernetes-iossl-prefer-server-ciphers">`nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iossl-prefer-server-ciphers" href="#opt-nginx-ingress-kubernetes-iossl-prefer-server-ciphers" title="#opt-nginx-ingress-kubernetes-iossl-prefer-server-ciphers">`nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioconnection-proxy-header" href="#opt-nginx-ingress-kubernetes-ioconnection-proxy-header" title="#opt-nginx-ingress-kubernetes-ioconnection-proxy-header">`nginx.ingress.kubernetes.io/connection-proxy-header`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioconnection-proxy-header" href="#opt-nginx-ingress-kubernetes-ioconnection-proxy-header" title="#opt-nginx-ingress-kubernetes-ioconnection-proxy-header">`nginx.ingress.kubernetes.io/connection-proxy-header`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioenable-access-log" href="#opt-nginx-ingress-kubernetes-ioenable-access-log" title="#opt-nginx-ingress-kubernetes-ioenable-access-log">`nginx.ingress.kubernetes.io/enable-access-log`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioenable-access-log" href="#opt-nginx-ingress-kubernetes-ioenable-access-log" title="#opt-nginx-ingress-kubernetes-ioenable-access-log">`nginx.ingress.kubernetes.io/enable-access-log`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioenable-opentracing" href="#opt-nginx-ingress-kubernetes-ioenable-opentracing" title="#opt-nginx-ingress-kubernetes-ioenable-opentracing">`nginx.ingress.kubernetes.io/enable-opentracing`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioenable-opentracing" href="#opt-nginx-ingress-kubernetes-ioenable-opentracing" title="#opt-nginx-ingress-kubernetes-ioenable-opentracing">`nginx.ingress.kubernetes.io/enable-opentracing`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioopentracing-trust-incoming-span" href="#opt-nginx-ingress-kubernetes-ioopentracing-trust-incoming-span" title="#opt-nginx-ingress-kubernetes-ioopentracing-trust-incoming-span">`nginx.ingress.kubernetes.io/opentracing-trust-incoming-span`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioopentracing-trust-incoming-span" href="#opt-nginx-ingress-kubernetes-ioopentracing-trust-incoming-span" title="#opt-nginx-ingress-kubernetes-ioopentracing-trust-incoming-span">`nginx.ingress.kubernetes.io/opentracing-trust-incoming-span`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioenable-opentelemetry" href="#opt-nginx-ingress-kubernetes-ioenable-opentelemetry" title="#opt-nginx-ingress-kubernetes-ioenable-opentelemetry">`nginx.ingress.kubernetes.io/enable-opentelemetry`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioenable-opentelemetry" href="#opt-nginx-ingress-kubernetes-ioenable-opentelemetry" title="#opt-nginx-ingress-kubernetes-ioenable-opentelemetry">`nginx.ingress.kubernetes.io/enable-opentelemetry`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioopentelemetry-trust-incoming-span" href="#opt-nginx-ingress-kubernetes-ioopentelemetry-trust-incoming-span" title="#opt-nginx-ingress-kubernetes-ioopentelemetry-trust-incoming-span">`nginx.ingress.kubernetes.io/opentelemetry-trust-incoming-span`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioopentelemetry-trust-incoming-span" href="#opt-nginx-ingress-kubernetes-ioopentelemetry-trust-incoming-span" title="#opt-nginx-ingress-kubernetes-ioopentelemetry-trust-incoming-span">`nginx.ingress.kubernetes.io/opentelemetry-trust-incoming-span`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioenable-modsecurity" href="#opt-nginx-ingress-kubernetes-ioenable-modsecurity" title="#opt-nginx-ingress-kubernetes-ioenable-modsecurity">`nginx.ingress.kubernetes.io/enable-modsecurity`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioenable-modsecurity" href="#opt-nginx-ingress-kubernetes-ioenable-modsecurity" title="#opt-nginx-ingress-kubernetes-ioenable-modsecurity">`nginx.ingress.kubernetes.io/enable-modsecurity`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioenable-owasp-core-rules" href="#opt-nginx-ingress-kubernetes-ioenable-owasp-core-rules" title="#opt-nginx-ingress-kubernetes-ioenable-owasp-core-rules">`nginx.ingress.kubernetes.io/enable-owasp-core-rules`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioenable-owasp-core-rules" href="#opt-nginx-ingress-kubernetes-ioenable-owasp-core-rules" title="#opt-nginx-ingress-kubernetes-ioenable-owasp-core-rules">`nginx.ingress.kubernetes.io/enable-owasp-core-rules`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iomodsecurity-transaction-id" href="#opt-nginx-ingress-kubernetes-iomodsecurity-transaction-id" title="#opt-nginx-ingress-kubernetes-iomodsecurity-transaction-id">`nginx.ingress.kubernetes.io/modsecurity-transaction-id`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iomodsecurity-transaction-id" href="#opt-nginx-ingress-kubernetes-iomodsecurity-transaction-id" title="#opt-nginx-ingress-kubernetes-iomodsecurity-transaction-id">`nginx.ingress.kubernetes.io/modsecurity-transaction-id`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iomodsecurity-snippet" href="#opt-nginx-ingress-kubernetes-iomodsecurity-snippet" title="#opt-nginx-ingress-kubernetes-iomodsecurity-snippet">`nginx.ingress.kubernetes.io/modsecurity-snippet`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iomodsecurity-snippet" href="#opt-nginx-ingress-kubernetes-iomodsecurity-snippet" title="#opt-nginx-ingress-kubernetes-iomodsecurity-snippet">`nginx.ingress.kubernetes.io/modsecurity-snippet`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iomirror-request-body" href="#opt-nginx-ingress-kubernetes-iomirror-request-body" title="#opt-nginx-ingress-kubernetes-iomirror-request-body">`nginx.ingress.kubernetes.io/mirror-request-body`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iomirror-request-body" href="#opt-nginx-ingress-kubernetes-iomirror-request-body" title="#opt-nginx-ingress-kubernetes-iomirror-request-body">`nginx.ingress.kubernetes.io/mirror-request-body`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iomirror-target" href="#opt-nginx-ingress-kubernetes-iomirror-target" title="#opt-nginx-ingress-kubernetes-iomirror-target">`nginx.ingress.kubernetes.io/mirror-target`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iomirror-target" href="#opt-nginx-ingress-kubernetes-iomirror-target" title="#opt-nginx-ingress-kubernetes-iomirror-target">`nginx.ingress.kubernetes.io/mirror-target`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iomirror-host" href="#opt-nginx-ingress-kubernetes-iomirror-host" title="#opt-nginx-ingress-kubernetes-iomirror-host">`nginx.ingress.kubernetes.io/mirror-host`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iomirror-host" href="#opt-nginx-ingress-kubernetes-iomirror-host" title="#opt-nginx-ingress-kubernetes-iomirror-host">`nginx.ingress.kubernetes.io/mirror-host`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iox-forwarded-prefix" href="#opt-nginx-ingress-kubernetes-iox-forwarded-prefix" title="#opt-nginx-ingress-kubernetes-iox-forwarded-prefix">`nginx.ingress.kubernetes.io/x-forwarded-prefix`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iox-forwarded-prefix" href="#opt-nginx-ingress-kubernetes-iox-forwarded-prefix" title="#opt-nginx-ingress-kubernetes-iox-forwarded-prefix">`nginx.ingress.kubernetes.io/x-forwarded-prefix`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioupstream-hash-by" href="#opt-nginx-ingress-kubernetes-ioupstream-hash-by" title="#opt-nginx-ingress-kubernetes-ioupstream-hash-by">`nginx.ingress.kubernetes.io/upstream-hash-by`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioupstream-hash-by" href="#opt-nginx-ingress-kubernetes-ioupstream-hash-by" title="#opt-nginx-ingress-kubernetes-ioupstream-hash-by">`nginx.ingress.kubernetes.io/upstream-hash-by`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioupstream-vhost" href="#opt-nginx-ingress-kubernetes-ioupstream-vhost" title="#opt-nginx-ingress-kubernetes-ioupstream-vhost">`nginx.ingress.kubernetes.io/upstream-vhost`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioupstream-vhost" href="#opt-nginx-ingress-kubernetes-ioupstream-vhost" title="#opt-nginx-ingress-kubernetes-ioupstream-vhost">`nginx.ingress.kubernetes.io/upstream-vhost`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iodenylist-source-range" href="#opt-nginx-ingress-kubernetes-iodenylist-source-range" title="#opt-nginx-ingress-kubernetes-iodenylist-source-range">`nginx.ingress.kubernetes.io/denylist-source-range`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iodenylist-source-range" href="#opt-nginx-ingress-kubernetes-iodenylist-source-range" title="#opt-nginx-ingress-kubernetes-iodenylist-source-range">`nginx.ingress.kubernetes.io/denylist-source-range`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iowhitelist-source-range" href="#opt-nginx-ingress-kubernetes-iowhitelist-source-range" title="#opt-nginx-ingress-kubernetes-iowhitelist-source-range">`nginx.ingress.kubernetes.io/whitelist-source-range`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iowhitelist-source-range" href="#opt-nginx-ingress-kubernetes-iowhitelist-source-range" title="#opt-nginx-ingress-kubernetes-iowhitelist-source-range">`nginx.ingress.kubernetes.io/whitelist-source-range`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-buffering" href="#opt-nginx-ingress-kubernetes-ioproxy-buffering" title="#opt-nginx-ingress-kubernetes-ioproxy-buffering">`nginx.ingress.kubernetes.io/proxy-buffering`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-buffering" href="#opt-nginx-ingress-kubernetes-ioproxy-buffering" title="#opt-nginx-ingress-kubernetes-ioproxy-buffering">`nginx.ingress.kubernetes.io/proxy-buffering`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-buffers-number" href="#opt-nginx-ingress-kubernetes-ioproxy-buffers-number" title="#opt-nginx-ingress-kubernetes-ioproxy-buffers-number">`nginx.ingress.kubernetes.io/proxy-buffers-number`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-buffers-number" href="#opt-nginx-ingress-kubernetes-ioproxy-buffers-number" title="#opt-nginx-ingress-kubernetes-ioproxy-buffers-number">`nginx.ingress.kubernetes.io/proxy-buffers-number`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-buffer-size" href="#opt-nginx-ingress-kubernetes-ioproxy-buffer-size" title="#opt-nginx-ingress-kubernetes-ioproxy-buffer-size">`nginx.ingress.kubernetes.io/proxy-buffer-size`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-buffer-size" href="#opt-nginx-ingress-kubernetes-ioproxy-buffer-size" title="#opt-nginx-ingress-kubernetes-ioproxy-buffer-size">`nginx.ingress.kubernetes.io/proxy-buffer-size`</a> | |
| <a id="opt-nginx-ingress-kubernetes-ioproxy-max-temp-file-size" href="#opt-nginx-ingress-kubernetes-ioproxy-max-temp-file-size" title="#opt-nginx-ingress-kubernetes-ioproxy-max-temp-file-size">`nginx.ingress.kubernetes.io/proxy-max-temp-file-size`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-ioproxy-max-temp-file-size" href="#opt-nginx-ingress-kubernetes-ioproxy-max-temp-file-size" title="#opt-nginx-ingress-kubernetes-ioproxy-max-temp-file-size">`nginx.ingress.kubernetes.io/proxy-max-temp-file-size`</a> | |
| <a id="opt-nginx-ingress-kubernetes-iostream-snippet" href="#opt-nginx-ingress-kubernetes-iostream-snippet" title="#opt-nginx-ingress-kubernetes-iostream-snippet">`nginx.ingress.kubernetes.io/stream-snippet`</a> | Not supported yet. | | <a id="opt-nginx-ingress-kubernetes-iostream-snippet" href="#opt-nginx-ingress-kubernetes-iostream-snippet" title="#opt-nginx-ingress-kubernetes-iostream-snippet">`nginx.ingress.kubernetes.io/stream-snippet`</a> | |
### Global Configuration
Traefik does not expose all global configuration options to control default behaviors for Ingresses in the same way NGINX does.
Some behaviors that are globally configurable in NGINX (such as default SSL redirect, rate limiting, or affinity) are currently not supported and cannot be overridden per-Ingress as in NGINX. These limitations are noted in the annotation tables below where applicable.

View File

@@ -285,6 +285,15 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
"traefik.http.services.myservice.loadbalancer.server.scheme=http" "traefik.http.services.myservice.loadbalancer.server.scheme=http"
``` ```
??? info "`traefik.http.services.<service_name>.loadbalancer.server.url`"
Defines the service URL.
This option cannot be used in combination with `port` or `scheme` definition.
```yaml
traefik.http.services.<service_name>.loadbalancer.server.url=http://foobar:8080
```
??? info "`traefik.http.services.<service_name>.loadbalancer.serverstransport`" ??? info "`traefik.http.services.<service_name>.loadbalancer.serverstransport`"
Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one.

View File

@@ -301,6 +301,15 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
- "traefik.http.services.myservice.loadbalancer.server.scheme=http" - "traefik.http.services.myservice.loadbalancer.server.scheme=http"
``` ```
??? info "`traefik.http.services.<service_name>.loadbalancer.server.url`"
Defines the service URL.
This option cannot be used in combination with `port` or `scheme` definition.
```yaml
traefik.http.services.<service_name>.loadbalancer.server.url=http://foobar:8080
```
??? info "`traefik.http.services.<service_name>.loadbalancer.server.weight`" ??? info "`traefik.http.services.<service_name>.loadbalancer.server.weight`"
Overrides the default weight. Overrides the default weight.

View File

@@ -255,7 +255,7 @@ HighestRandomWeight, also called RendezVous hashing allows to loadbalance client
services: services:
my-service: my-service:
loadBalancer: loadBalancer:
type: hrw strategy: hrw
servers: servers:
- url: "http://private-ip-server-1/" - url: "http://private-ip-server-1/"
- url: "http://private-ip-server-2/" - url: "http://private-ip-server-2/"
@@ -1306,13 +1306,13 @@ http:
appv1: appv1:
loadBalancer: loadBalancer:
type: hrw strategy: hrw
servers: servers:
- url: "http://private-ip-server-1/" - url: "http://private-ip-server-1/"
appv2: appv2:
loadBalancer: loadBalancer:
type: hrw strategy: hrw
servers: servers:
- url: "http://private-ip-server-2/" - url: "http://private-ip-server-2/"
``` ```
@@ -1330,13 +1330,13 @@ http:
[http.services.appv1] [http.services.appv1]
[http.services.appv1.loadBalancer] [http.services.appv1.loadBalancer]
type = "hrw" strategy = "hrw"
[[http.services.appv1.loadBalancer.servers]] [[http.services.appv1.loadBalancer.servers]]
url = "http://private-ip-server-1/" url = "http://private-ip-server-1/"
[http.services.appv2] [http.services.appv2]
[http.services.appv2.loadBalancer] [http.services.appv2.loadBalancer]
type = "hrw" strategy = "hrw"
[[http.services.appv2.loadBalancer.servers]] [[http.services.appv2.loadBalancer.servers]]
url = "http://private-ip-server-2/" url = "http://private-ip-server-2/"
``` ```
@@ -1371,7 +1371,7 @@ http:
appv1: appv1:
loadBalancer: loadBalancer:
type: hrw strategy: hrw
healthCheck: healthCheck:
path: /status path: /status
interval: 10s interval: 10s
@@ -1381,7 +1381,7 @@ http:
appv2: appv2:
loadBalancer: loadBalancer:
type: hrw strategy: hrw
healthCheck: healthCheck:
path: /status path: /status
interval: 10s interval: 10s
@@ -1404,7 +1404,7 @@ http:
[http.services.appv1] [http.services.appv1]
[http.services.appv1.loadBalancer] [http.services.appv1.loadBalancer]
type="hrw" strategy="hrw"
[http.services.appv1.loadBalancer.healthCheck] [http.services.appv1.loadBalancer.healthCheck]
path = "/health" path = "/health"
interval = "10s" interval = "10s"
@@ -1414,7 +1414,7 @@ http:
[http.services.appv2] [http.services.appv2]
[http.services.appv2.loadBalancer] [http.services.appv2.loadBalancer]
type="hrw" strategy="hrw"
[http.services.appv2.loadBalancer.healthCheck] [http.services.appv2.loadBalancer.healthCheck]
path = "/health" path = "/health"
interval = "10s" interval = "10s"

View File

@@ -1,5 +1,5 @@
server: server:
image: rancher/k3s:v1.17.2-k3s1 image: rancher/k3s:v1.34.2-k3s1
command: server --disable-agent --no-deploy traefik command: server --disable-agent --no-deploy traefik
environment: environment:
- K3S_CLUSTER_SECRET=somethingtotallyrandom - K3S_CLUSTER_SECRET=somethingtotallyrandom
@@ -17,7 +17,7 @@ server:
- 6443:6443 - 6443:6443
node: node:
image: rancher/k3s:v1.17.2-k3s1 image: rancher/k3s:v1.34.2-k3s1
privileged: true privileged: true
links: links:
- server - server

22
go.mod
View File

@@ -55,7 +55,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo. github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo.
github.com/prometheus/client_golang v1.23.0 github.com/prometheus/client_golang v1.23.0
github.com/prometheus/client_model v0.6.2 github.com/prometheus/client_model v0.6.2
github.com/quic-go/quic-go v0.55.0 github.com/quic-go/quic-go v0.57.0
github.com/redis/go-redis/v9 v9.8.0 github.com/redis/go-redis/v9 v9.8.0
github.com/rs/zerolog v1.33.0 github.com/rs/zerolog v1.33.0
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
@@ -95,14 +95,14 @@ require (
go.opentelemetry.io/otel/sdk/log v0.14.0 go.opentelemetry.io/otel/sdk/log v0.14.0
go.opentelemetry.io/otel/sdk/metric v1.38.0 go.opentelemetry.io/otel/sdk/metric v1.38.0
go.opentelemetry.io/otel/trace v1.38.0 go.opentelemetry.io/otel/trace v1.38.0
golang.org/x/crypto v0.43.0 golang.org/x/crypto v0.45.0
golang.org/x/mod v0.28.0 golang.org/x/mod v0.29.0
golang.org/x/net v0.46.0 golang.org/x/net v0.47.0
golang.org/x/sync v0.17.0 golang.org/x/sync v0.18.0
golang.org/x/sys v0.37.0 golang.org/x/sys v0.38.0
golang.org/x/text v0.30.0 golang.org/x/text v0.31.0
golang.org/x/time v0.14.0 golang.org/x/time v0.14.0
golang.org/x/tools v0.37.0 golang.org/x/tools v0.38.0
google.golang.org/grpc v1.76.0 google.golang.org/grpc v1.76.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@@ -322,7 +322,7 @@ require (
github.com/pquerna/otp v1.5.0 // indirect github.com/pquerna/otp v1.5.0 // indirect
github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.17.0 // indirect github.com/prometheus/procfs v0.17.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.6.0 // indirect
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
github.com/rs/cors v1.7.0 // indirect github.com/rs/cors v1.7.0 // indirect
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 // indirect github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 // indirect
@@ -388,9 +388,9 @@ require (
go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.4.0 // indirect golang.org/x/arch v0.4.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect
golang.org/x/term v0.36.0 // indirect golang.org/x/term v0.37.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/api v0.254.0 // indirect google.golang.org/api v0.254.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect

44
go.sum
View File

@@ -1112,10 +1112,10 @@ github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUO
github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0=
github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= github.com/quic-go/quic-go v0.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE=
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
@@ -1507,8 +1507,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1522,8 +1522,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -1553,8 +1553,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1617,8 +1617,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1644,8 +1644,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1743,8 +1743,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1761,8 +1761,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1781,8 +1781,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1852,8 +1852,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -42,7 +42,7 @@ var (
) )
const ( const (
k3sImage = "docker.io/rancher/k3s:v1.32.9-k3s1" k3sImage = "docker.io/rancher/k3s:v1.34.2-k3s1"
traefikImage = "traefik/traefik:latest" traefikImage = "traefik/traefik:latest"
traefikDeployment = "deployments/traefik" traefikDeployment = "deployments/traefik"
traefikNamespace = "traefik" traefikNamespace = "traefik"

View File

@@ -1,6 +1,6 @@
services: services:
server: server:
image: rancher/k3s:v1.21.14-k3s1 image: rancher/k3s:v1.34.2-k3s1
privileged: true privileged: true
command: command:
- server - server
@@ -25,7 +25,7 @@ services:
- ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests - ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests
node: node:
image: rancher/k3s:v1.21.14-k3s1 image: rancher/k3s:v1.34.2-k3s1
privileged: true privileged: true
environment: environment:
K3S_TOKEN: somethingtotallyrandom K3S_TOKEN: somethingtotallyrandom

View File

@@ -568,8 +568,9 @@ func (i *ingress) deprecationNotice(logger zerolog.Logger) {
} }
type experimental struct { type experimental struct {
HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"` HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"`
KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"` KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"`
KubernetesIngressNGINX *bool `json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty"`
} }
func (e *experimental) deprecationNotice(logger zerolog.Logger) bool { func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
@@ -591,6 +592,12 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#gateway-api-kubernetesgateway-provider") " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#gateway-api-kubernetesgateway-provider")
} }
if e.KubernetesIngressNGINX != nil {
logger.Error().Msg("KubernetesIngressNGINX provider is not an experimental feature starting with v3.6.2." +
" Please remove its usage from the install configuration." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#ingress-nginx-provider")
}
return false return false
} }

View File

@@ -4,14 +4,15 @@ import "github.com/traefik/traefik/v3/pkg/plugins"
// Experimental experimental Traefik features. // Experimental experimental Traefik features.
type Experimental struct { type Experimental struct {
Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"` Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"`
LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"` LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"`
AbortOnPluginFailure bool `description:"Defines whether all plugins must be loaded successfully for Traefik to start." json:"abortOnPluginFailure,omitempty" toml:"abortOnPluginFailure,omitempty" yaml:"abortOnPluginFailure,omitempty" export:"true"` AbortOnPluginFailure bool `description:"Defines whether all plugins must be loaded successfully for Traefik to start." json:"abortOnPluginFailure,omitempty" toml:"abortOnPluginFailure,omitempty" yaml:"abortOnPluginFailure,omitempty" export:"true"`
FastProxy *FastProxyConfig `description:"Enables the FastProxy implementation." json:"fastProxy,omitempty" toml:"fastProxy,omitempty" yaml:"fastProxy,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` FastProxy *FastProxyConfig `description:"Enables the FastProxy implementation." json:"fastProxy,omitempty" toml:"fastProxy,omitempty" yaml:"fastProxy,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
OTLPLogs bool `description:"Enables the OpenTelemetry logs integration." json:"otlplogs,omitempty" toml:"otlplogs,omitempty" yaml:"otlplogs,omitempty" export:"true"` OTLPLogs bool `description:"Enables the OpenTelemetry logs integration." json:"otlplogs,omitempty" toml:"otlplogs,omitempty" yaml:"otlplogs,omitempty" export:"true"`
Knative bool `description:"Allow the Knative provider usage." json:"knative,omitempty" toml:"knative,omitempty" yaml:"knative,omitempty" export:"true"` Knative bool `description:"Allow the Knative provider usage." json:"knative,omitempty" toml:"knative,omitempty" yaml:"knative,omitempty" export:"true"`
KubernetesIngressNGINX bool `description:"Allow the Kubernetes Ingress NGINX provider usage." json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty" export:"true"`
// Deprecated: KubernetesIngressNGINX provider is not an experimental feature starting with v3.6.2. Please remove its usage from the static configuration.
KubernetesIngressNGINX bool `description:"Allow the Kubernetes Ingress NGINX provider usage." json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty" export:"true"`
// Deprecated: KubernetesGateway provider is not an experimental feature starting with v3.1. Please remove its usage from the static configuration. // Deprecated: KubernetesGateway provider is not an experimental feature starting with v3.1. Please remove its usage from the static configuration.
KubernetesGateway bool `description:"(Deprecated) Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"` KubernetesGateway bool `description:"(Deprecated) Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"`
} }

View File

@@ -424,10 +424,6 @@ func (c *Configuration) ValidateConfiguration() error {
} }
if c.Providers != nil && c.Providers.KubernetesIngressNGINX != nil { if c.Providers != nil && c.Providers.KubernetesIngressNGINX != nil {
if c.Experimental == nil || !c.Experimental.KubernetesIngressNGINX {
return errors.New("the experimental KubernetesIngressNGINX feature must be enabled to use the KubernetesIngressNGINX provider")
}
if c.Providers.KubernetesIngressNGINX.WatchNamespace != "" && c.Providers.KubernetesIngressNGINX.WatchNamespaceSelector != "" { if c.Providers.KubernetesIngressNGINX.WatchNamespace != "" && c.Providers.KubernetesIngressNGINX.WatchNamespaceSelector != "" {
return errors.New("watchNamespace and watchNamespaceSelector options are mutually exclusive") return errors.New("watchNamespace and watchNamespaceSelector options are mutually exclusive")
} }

View File

@@ -8,6 +8,7 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"golang.org/x/mod/module"
) )
const localGoPath = "./plugins-local/" const localGoPath = "./plugins-local/"
@@ -53,24 +54,18 @@ func checkRemotePluginsConfiguration(plugins map[string]Descriptor) error {
var errs []string var errs []string
for pAlias, descriptor := range plugins { for pAlias, descriptor := range plugins {
if descriptor.ModuleName == "" { if err := module.CheckPath(descriptor.ModuleName); err != nil {
errs = append(errs, fmt.Sprintf("%s: plugin name is missing", pAlias)) errs = append(errs, fmt.Sprintf("%s: malformed plugin module name is missing: %s", pAlias, err))
} }
if descriptor.Version == "" { if descriptor.Version == "" {
errs = append(errs, fmt.Sprintf("%s: plugin version is missing", pAlias)) errs = append(errs, fmt.Sprintf("%s: plugin version is missing", pAlias))
} }
if strings.HasPrefix(descriptor.ModuleName, "/") || strings.HasSuffix(descriptor.ModuleName, "/") {
errs = append(errs, fmt.Sprintf("%s: plugin name should not start or end with a /", pAlias))
continue
}
if _, ok := uniq[descriptor.ModuleName]; ok { if _, ok := uniq[descriptor.ModuleName]; ok {
errs = append(errs, fmt.Sprintf("only one version of a plugin is allowed, there is a duplicate of %s", descriptor.ModuleName)) errs = append(errs, fmt.Sprintf("only one version of a plugin is allowed, there is a duplicate of %s", descriptor.ModuleName))
continue continue
} }
uniq[descriptor.ModuleName] = struct{}{} uniq[descriptor.ModuleName] = struct{}{}
} }

View File

@@ -0,0 +1,85 @@
package plugins
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_checkRemotePluginsConfiguration(t *testing.T) {
testCases := []struct {
name string
plugins map[string]Descriptor
wantErr bool
}{
{
name: "nil plugins configuration returns no error",
plugins: nil,
wantErr: false,
},
{
name: "malformed module name returns error",
plugins: map[string]Descriptor{
"plugin1": {ModuleName: "invalid/module/name", Version: "v1.0.0"},
},
wantErr: true,
},
{
name: "malformed module name with path traversal returns error",
plugins: map[string]Descriptor{
"plugin1": {ModuleName: "github.com/module/../name", Version: "v1.0.0"},
},
wantErr: true,
},
{
name: "malformed module name with encoded path traversal returns error",
plugins: map[string]Descriptor{
"plugin1": {ModuleName: "github.com/module%2F%2E%2E%2Fname", Version: "v1.0.0"},
},
wantErr: true,
},
{
name: "malformed module name returns error",
plugins: map[string]Descriptor{
"plugin1": {ModuleName: "invalid/module/name", Version: "v1.0.0"},
},
wantErr: true,
},
{
name: "missing plugin version returns error",
plugins: map[string]Descriptor{
"plugin1": {ModuleName: "github.com/module/name", Version: ""},
},
wantErr: true,
},
{
name: "duplicate plugin module name returns error",
plugins: map[string]Descriptor{
"plugin1": {ModuleName: "github.com/module/name", Version: "v1.0.0"},
"plugin2": {ModuleName: "github.com/module/name", Version: "v1.1.0"},
},
wantErr: true,
},
{
name: "valid plugins configuration returns no error",
plugins: map[string]Descriptor{
"plugin1": {ModuleName: "github.com/module/name1", Version: "v1.0.0"},
"plugin2": {ModuleName: "github.com/module/name2", Version: "v1.1.0"},
},
wantErr: false,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
err := checkRemotePluginsConfiguration(test.plugins)
if test.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -4,11 +4,11 @@ RepositoryName = "traefik"
OutputType = "file" OutputType = "file"
FileName = "traefik_changelog.md" FileName = "traefik_changelog.md"
# example new bugfix v3.6.1 # example new bugfix v3.6.2
CurrentRef = "v3.6" CurrentRef = "v3.6"
PreviousRef = "v3.6.0" PreviousRef = "v3.6.1"
BaseBranch = "v3.6" BaseBranch = "v3.6"
FutureCurrentRefName = "v3.6.1" FutureCurrentRefName = "v3.6.2"
ThresholdPreviousRef = 10 ThresholdPreviousRef = 10
ThresholdCurrentRef = 10 ThresholdCurrentRef = 10

View File

@@ -1 +1,2 @@
nodeLinker: node-modules nodeLinker: node-modules
enableScripts: false

View File

@@ -1,6 +1,7 @@
import { AriaTr, VariantProps, styled } from '@traefiklabs/faency' import { AriaTr, VariantProps, styled } from '@traefiklabs/faency'
import { ComponentProps, forwardRef, ReactNode } from 'react' import { ComponentProps, forwardRef, ReactNode } from 'react'
import { useHref } from 'react-router-dom'
import { useHrefWithReturnTo } from 'hooks/use-href-with-return-to'
const UnstyledLink = styled('a', { const UnstyledLink = styled('a', {
color: 'inherit', color: 'inherit',
@@ -18,7 +19,7 @@ type ClickableRowProps = ComponentProps<typeof AriaTr> &
} }
export default forwardRef<HTMLTableRowElement | null, ClickableRowProps>(({ children, css, to, ...props }, ref) => { export default forwardRef<HTMLTableRowElement | null, ClickableRowProps>(({ children, css, to, ...props }, ref) => {
const href = useHref(to) const href = useHrefWithReturnTo(to)
return ( return (
<AriaTr asChild interactive ref={ref} css={css} {...props}> <AriaTr asChild interactive ref={ref} css={css} {...props}>

View File

@@ -7,6 +7,7 @@ import { StatusWrapper } from './ResourceStatus'
import { colorByStatus } from './Status' import { colorByStatus } from './Status'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { useGetUrlWithReturnTo } from 'hooks/use-href-with-return-to'
const CustomHeading = styled(H2, { const CustomHeading = styled(H2, {
display: 'flex', display: 'flex',
@@ -125,9 +126,25 @@ const CardSkeleton = ({ bigDescription }: { bigDescription?: boolean }) => {
) )
} }
export const CardListSection = ({ icon, title, cards, isLast, bigDescription }: SectionType) => { const CardItem = ({ card }) => {
const navigate = useNavigate() const navigate = useNavigate()
const href = useGetUrlWithReturnTo(card.link)
return (
<SpacedCard key={card.description} css={{ border: card.focus ? `2px solid $primary` : '', p: '$3' }}>
<FlexLink
data-testid={card.link}
onClick={(): false | void => !!card.link && navigate(href)}
css={{ cursor: card.link ? 'pointer' : 'inherit' }}
>
<ItemTitle>{card.title}</ItemTitle>
<CardDescription>{card.description}</CardDescription>
</FlexLink>
</SpacedCard>
)
}
export const CardListSection = ({ icon, title, cards, isLast, bigDescription }: SectionType) => {
return ( return (
<Flex css={{ flexDirection: 'column', flexGrow: 1 }}> <Flex css={{ flexDirection: 'column', flexGrow: 1 }}>
<SectionHeader icon={icon} title={title} /> <SectionHeader icon={icon} title={title} />
@@ -135,20 +152,7 @@ export const CardListSection = ({ icon, title, cards, isLast, bigDescription }:
<CardListColumn> <CardListColumn>
<Flex css={{ flexDirection: 'column', flexGrow: 1, marginRight: '$3' }}> <Flex css={{ flexDirection: 'column', flexGrow: 1, marginRight: '$3' }}>
{!cards && <CardSkeleton bigDescription={bigDescription} />} {!cards && <CardSkeleton bigDescription={bigDescription} />}
{cards {cards?.filter((c) => !!c.description).map((card, idx) => <CardItem key={`card-${idx}`} card={card} />)}
?.filter((c) => !!c.description)
.map((card) => (
<SpacedCard key={card.description} css={{ border: card.focus ? `2px solid $primary` : '', p: '$3' }}>
<FlexLink
data-testid={card.link}
onClick={(): false | void => !!card.link && navigate(card.link)}
css={{ cursor: card.link ? 'pointer' : 'inherit' }}
>
<ItemTitle>{card.title}</ItemTitle>
<CardDescription>{card.description}</CardDescription>
</FlexLink>
</SpacedCard>
))}
<Box css={{ height: '16px' }}>&nbsp;</Box> <Box css={{ height: '16px' }}>&nbsp;</Box>
</Flex> </Flex>
</CardListColumn> </CardListColumn>

View File

@@ -0,0 +1,238 @@
import { renderHook } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { useGetUrlWithReturnTo, useHrefWithReturnTo, useRouterReturnTo } from './use-href-with-return-to'
describe('useGetUrlWithReturnTo', () => {
const createWrapper = (initialPath = '/') => {
return ({ children }) => <MemoryRouter initialEntries={[initialPath]}>{children}</MemoryRouter>
}
it('should append current path as returnTo query param', () => {
const { result } = renderHook(() => useGetUrlWithReturnTo('/target'), {
wrapper: createWrapper('/current/path'),
})
expect(result.current).toBe('/target?returnTo=%2Fcurrent%2Fpath')
})
it('should append current path with search params as returnTo', () => {
const { result } = renderHook(() => useGetUrlWithReturnTo('/target'), {
wrapper: createWrapper('/current/path?foo=bar'),
})
expect(result.current).toBe('/target?returnTo=%2Fcurrent%2Fpath%3Ffoo%3Dbar')
})
it('should use initialReturnTo when provided', () => {
const { result } = renderHook(() => useGetUrlWithReturnTo('/target', '/custom/return'), {
wrapper: createWrapper('/current/path'),
})
expect(result.current).toBe('/target?returnTo=%2Fcustom%2Freturn')
})
it('should return the href as-is when href is empty string', () => {
const { result } = renderHook(() => useGetUrlWithReturnTo(''), {
wrapper: createWrapper('/current/path'),
})
expect(result.current).toBe('')
})
it('should handle href with existing query params', () => {
const { result } = renderHook(() => useGetUrlWithReturnTo('/target?existing=param'), {
wrapper: createWrapper('/current/path'),
})
expect(result.current).toBe('/target?existing=param&returnTo=%2Fcurrent%2Fpath')
})
})
describe('useHrefWithReturnTo', () => {
const createWrapper = (initialPath = '/') => {
return ({ children }) => <MemoryRouter initialEntries={[initialPath]}>{children}</MemoryRouter>
}
it('should return resolved href with returnTo param containing current path', () => {
const { result } = renderHook(() => useHrefWithReturnTo('/target'), {
wrapper: createWrapper('/current'),
})
expect(result.current).toBe('/target?returnTo=%2Fcurrent')
})
it('should include current search params in returnTo', () => {
const { result } = renderHook(() => useHrefWithReturnTo('/target'), {
wrapper: createWrapper('/current?foo=bar&baz=qux'),
})
expect(result.current).toBe('/target?returnTo=%2Fcurrent%3Ffoo%3Dbar%26baz%3Dqux')
})
it('should use custom returnTo when provided instead of current path', () => {
const { result } = renderHook(() => useHrefWithReturnTo('/target', '/custom/return'), {
wrapper: createWrapper('/current'),
})
expect(result.current).toBe('/target?returnTo=%2Fcustom%2Freturn')
})
it('should handle absolute paths correctly', () => {
const { result } = renderHook(() => useHrefWithReturnTo('/http/routers'), {
wrapper: createWrapper('/tcp/services'),
})
expect(result.current).toBe('/http/routers?returnTo=%2Ftcp%2Fservices')
})
it('should preserve existing query params in target href', () => {
const { result } = renderHook(() => useHrefWithReturnTo('/target?existing=param'), {
wrapper: createWrapper('/current'),
})
expect(result.current).toBe('/target?existing=param&returnTo=%2Fcurrent')
})
it('should return root path when href is empty', () => {
const { result } = renderHook(() => useHrefWithReturnTo(''), {
wrapper: createWrapper('/current'),
})
// useHref converts empty string to root path
expect(result.current).toBe('/')
})
it('should handle complex nested paths in returnTo', () => {
const { result } = renderHook(() => useHrefWithReturnTo('/target'), {
wrapper: createWrapper('/http/routers/my-router-123'),
})
expect(result.current).toBe('/target?returnTo=%2Fhttp%2Frouters%2Fmy-router-123')
})
})
describe('useRouterReturnTo', () => {
const createWrapper = (initialPath = '/') => {
return ({ children }) => <MemoryRouter initialEntries={[initialPath]}>{children}</MemoryRouter>
}
it('should return null when no returnTo query param exists', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current'),
})
expect(result.current.returnTo).toBeNull()
expect(result.current.returnToLabel).toBeNull()
})
it('should extract returnTo from query params', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http/routers'),
})
expect(result.current.returnTo).toBe('/http/routers')
})
it('should generate correct label for HTTP routers (plural)', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http/routers'),
})
expect(result.current.returnToLabel).toBe('HTTP routers')
})
it('should generate correct label for HTTP router (singular)', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http/routers/router-1'),
})
expect(result.current.returnToLabel).toBe('HTTP router')
})
it('should generate fallback label for unknown routes (plural)', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/custom/resources'),
})
expect(result.current.returnToLabel).toBe('Custom resources')
})
it('should handle malformed returnTo paths gracefully', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/'),
})
expect(result.current.returnTo).toBe('/')
expect(result.current.returnToLabel).toBe('Back')
})
it('should handle returnTo with query params', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http/routers?filter=test'),
})
expect(result.current.returnTo).toContain('/http/routers')
expect(result.current.returnToLabel).toBe('HTTP routers')
})
it('should strip query params from path when generating label', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http/routers?filter=test&status=active'),
})
expect(result.current.returnToLabel).toBe('HTTP routers')
expect(result.current.returnToLabel).not.toContain('filter')
expect(result.current.returnToLabel).not.toContain('status')
})
it('should strip query params from subpath when generating label', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/tcp/services?page=2'),
})
expect(result.current.returnToLabel).toBe('TCP services')
})
it('should handle query params with multiple question marks gracefully', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http/routers?filter=test?extra=param'),
})
// Should handle edge case with multiple question marks (invalid URL but should not crash)
expect(result.current.returnToLabel).toBe('HTTP routers')
})
it('should handle path with query params but no subpath', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http?foo=bar'),
})
expect(result.current.returnToLabel).toBe('Http')
})
it('should handle empty query string (path ending with ?)', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/tcp/middlewares?'),
})
expect(result.current.returnToLabel).toBe('TCP middlewares')
})
it('should handle complex query strings with special characters', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/http/services?filter=%40test%23special'),
})
expect(result.current.returnToLabel).toBe('HTTP services')
})
it('should capitalize first letter of label override', () => {
const { result } = renderHook(() => useRouterReturnTo(), {
wrapper: createWrapper('/current?returnTo=/resource/routers/router-1'),
})
// Verify the label starts with uppercase
expect(result.current.returnToLabel?.charAt(0)).toBe('R')
})
})

View File

@@ -0,0 +1,119 @@
import qs from 'query-string'
import { useMemo } from 'react'
import { useHref, useLocation, useSearchParams } from 'react-router-dom'
import { capitalizeFirstLetter } from '../utils/string'
type UseGetUrlWithReturnTo = (href: string, initialReturnTo?: string) => string
export const useGetUrlWithReturnTo: UseGetUrlWithReturnTo = (href, initialReturnTo) => {
const location = useLocation()
const currentPath = location.pathname + location.search
const url = useMemo(() => {
if (href) {
return qs.stringifyUrl({ url: href, query: { returnTo: initialReturnTo ?? currentPath } })
}
return href
}, [currentPath, href, initialReturnTo])
return url
}
export const useHrefWithReturnTo = (href: string, returnTo?: string): string => {
const urlWithReturnTo = useGetUrlWithReturnTo(href, returnTo)
return useHref(urlWithReturnTo)
}
const RETURN_TO_LABEL_OVERRIDES_SINGULAR: Record<string, Record<string, string>> = {
http: {
routers: 'HTTP router',
services: 'HTTP service',
middlewares: 'HTTP middleware',
},
tcp: {
routers: 'TCP router',
services: 'TCP service',
middlewares: 'TCP middleware',
},
udp: {
routers: 'UDP router',
services: 'TCP service',
},
}
const RETURN_TO_LABEL_OVERRIDES_PLURAL: Record<string, Record<string, string>> = {
http: {
routers: 'HTTP routers',
services: 'HTTP services',
middlewares: 'HTTP middlewares',
},
tcp: {
routers: 'TCP routers',
services: 'TCP services',
middlewares: 'TCP middlewares',
},
udp: {
routers: 'UDP routers',
services: 'TCP services',
},
}
type UseRouterReturnTo = (initialReturnTo?: string) => {
returnTo: string | null
returnToLabel: string | null
}
const getCleanPath = (path: string) => {
if (!path) return ''
return path.split('?')[0]
}
export const useRouterReturnTo: UseRouterReturnTo = () => {
const [searchParams] = useSearchParams()
const returnTo = useMemo(() => {
const queryReturnTo = searchParams.get('returnTo')
return queryReturnTo || null
}, [searchParams])
const returnToHref = useHref(returnTo || '')
const returnToLabel = useMemo(() => {
if (!returnTo) {
return null
}
const returnToArr = returnTo.split('/')
const [, path, subpath, id] = returnToArr
// Strip query params from path, if any
const cleanPath = getCleanPath(path)
const cleanSubpath = getCleanPath(subpath)
// Malformed returnTo (e.g., just '/' or empty path)
if (!cleanPath) {
return 'Back'
}
const fallbackLabel = `${capitalizeFirstLetter(cleanPath)}${cleanSubpath ? ` ${cleanSubpath}` : ''}`
const labelArray = id ? RETURN_TO_LABEL_OVERRIDES_SINGULAR : RETURN_TO_LABEL_OVERRIDES_PLURAL
const labelOverride =
labelArray[cleanPath]?.[cleanSubpath] ??
(typeof labelArray[cleanPath] === 'string' ? labelArray[cleanPath] : fallbackLabel)
return capitalizeFirstLetter(labelOverride)
}, [returnTo])
return useMemo(
() => ({
returnTo: returnTo ? returnToHref : null,
returnToLabel,
}),
[returnTo, returnToHref, returnToLabel],
)
}

View File

@@ -1,96 +0,0 @@
import { waitFor } from '@testing-library/react'
import { SideNav, TopNav } from './Navigation'
import useHubUpgradeButton from 'hooks/use-hub-upgrade-button'
import { renderWithProviders } from 'utils/test'
vi.mock('hooks/use-hub-upgrade-button')
const mockUseHubUpgradeButton = vi.mocked(useHubUpgradeButton)
describe('Navigation', () => {
beforeEach(() => {
mockUseHubUpgradeButton.mockReturnValue({
signatureVerified: false,
scriptBlobUrl: null,
isCustomElementDefined: false,
})
})
afterEach(() => {
vi.clearAllMocks()
})
it('should render the side navigation bar', async () => {
const { container } = renderWithProviders(<SideNav isExpanded={false} onSidePanelToggle={() => {}} />)
expect(container.innerHTML).toContain('HTTP')
expect(container.innerHTML).toContain('TCP')
expect(container.innerHTML).toContain('UDP')
expect(container.innerHTML).toContain('Plugins')
})
it('should render the top navigation bar', async () => {
const { container } = renderWithProviders(<TopNav />)
expect(container.innerHTML).toContain('theme-switcher')
expect(container.innerHTML).toContain('help-menu')
})
describe('hub-button-app rendering', () => {
it('should NOT render hub-button-app when signatureVerified is false', async () => {
mockUseHubUpgradeButton.mockReturnValue({
signatureVerified: false,
scriptBlobUrl: null,
isCustomElementDefined: false,
})
const { container } = renderWithProviders(<TopNav />)
const hubButtonApp = container.querySelector('hub-button-app')
expect(hubButtonApp).toBeNull()
})
it('should NOT render hub-button-app when scriptBlobUrl is null', async () => {
mockUseHubUpgradeButton.mockReturnValue({
signatureVerified: true,
scriptBlobUrl: null,
isCustomElementDefined: false,
})
const { container } = renderWithProviders(<TopNav />)
const hubButtonApp = container.querySelector('hub-button-app')
expect(hubButtonApp).toBeNull()
})
it('should render hub-button-app when signatureVerified is true and scriptBlobUrl exists', async () => {
mockUseHubUpgradeButton.mockReturnValue({
signatureVerified: true,
scriptBlobUrl: 'blob:http://localhost:3000/mock-blob-url',
isCustomElementDefined: false,
})
const { container } = renderWithProviders(<TopNav />)
await waitFor(() => {
const hubButtonApp = container.querySelector('hub-button-app')
expect(hubButtonApp).not.toBeNull()
})
})
it('should NOT render hub-button-app when noHubButton prop is true', async () => {
mockUseHubUpgradeButton.mockReturnValue({
signatureVerified: true,
scriptBlobUrl: 'blob:http://localhost:3000/mock-blob-url',
isCustomElementDefined: false,
})
const { container } = renderWithProviders(<TopNav noHubButton={true} />)
const hubButtonApp = container.querySelector('hub-button-app')
expect(hubButtonApp).toBeNull()
})
})
})

View File

@@ -4,10 +4,10 @@ import { Helmet } from 'react-helmet-async'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import Container from './Container' import Container from './Container'
import { LAPTOP_BP, SideBarPanel, SideNav, TopNav } from './Navigation'
import { ToastPool } from 'components/ToastPool' import { ToastPool } from 'components/ToastPool'
import { ToastProvider } from 'contexts/toasts' import { ToastProvider } from 'contexts/toasts'
import { LAPTOP_BP, SideBarPanel, SideNav, TopNav } from 'layout/navigation'
export const LIGHT_PRIMARY_COLOR = '#217F97' export const LIGHT_PRIMARY_COLOR = '#217F97'
export const DARK_PRIMARY_COLOR = '#2AA2C1' export const DARK_PRIMARY_COLOR = '#2AA2C1'

View File

@@ -0,0 +1,21 @@
import { SideNav, TopNav } from '.'
import { renderWithProviders } from 'utils/test'
describe('Navigation', () => {
it('should render the side navigation bar', async () => {
const { container } = renderWithProviders(<SideNav isExpanded onSidePanelToggle={() => {}} />)
expect(container.innerHTML).toContain('HTTP')
expect(container.innerHTML).toContain('TCP')
expect(container.innerHTML).toContain('UDP')
expect(container.innerHTML).toContain('Plugins')
})
it('should render the top navigation bar', async () => {
const { container } = renderWithProviders(<TopNav />)
expect(container.innerHTML).toContain('theme-switcher')
expect(container.innerHTML).toContain('help-menu')
})
})

View File

@@ -1,18 +1,9 @@
import { import {
Badge, Badge,
Box, Box,
Button,
CSS,
DialogTitle, DialogTitle,
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuTrigger,
elevationVariants, elevationVariants,
Flex, Flex,
Link,
NavigationLink, NavigationLink,
SidePanel, SidePanel,
styled, styled,
@@ -21,30 +12,24 @@ import {
VisuallyHidden, VisuallyHidden,
} from '@traefiklabs/faency' } from '@traefiklabs/faency'
import { useContext, useEffect, useMemo, useState } from 'react' import { useContext, useEffect, useMemo, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { BsChevronDoubleRight, BsChevronDoubleLeft } from 'react-icons/bs' import { BsChevronDoubleRight, BsChevronDoubleLeft } from 'react-icons/bs'
import { FiBookOpen, FiGithub, FiHelpCircle } from 'react-icons/fi'
import { matchPath, useHref } from 'react-router' import { matchPath, useHref } from 'react-router'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { useWindowSize } from 'usehooks-ts' import { useWindowSize } from 'usehooks-ts'
import Container from './Container' import Container from '../Container'
import { DARK_PRIMARY_COLOR, LIGHT_PRIMARY_COLOR } from './Page'
import { LAPTOP_BP } from '.'
import IconButton from 'components/buttons/IconButton' import IconButton from 'components/buttons/IconButton'
import Logo from 'components/icons/Logo' import Logo from 'components/icons/Logo'
import { PluginsIcon } from 'components/icons/PluginsIcon' import { PluginsIcon } from 'components/icons/PluginsIcon'
import ThemeSwitcher from 'components/ThemeSwitcher'
import TooltipText from 'components/TooltipText' import TooltipText from 'components/TooltipText'
import { VersionContext } from 'contexts/version' import { VersionContext } from 'contexts/version'
import useHubUpgradeButton from 'hooks/use-hub-upgrade-button'
import useTotals from 'hooks/use-overview-totals' import useTotals from 'hooks/use-overview-totals'
import { useIsDarkMode } from 'hooks/use-theme'
import ApimDemoNavMenu from 'pages/hub-demo/HubDemoNav' import ApimDemoNavMenu from 'pages/hub-demo/HubDemoNav'
import { Route, ROUTES } from 'routes' import { Route, ROUTES } from 'routes'
export const LAPTOP_BP = 1025
const NavigationDrawer = styled(Flex, { const NavigationDrawer = styled(Flex, {
width: '100%', width: '100%',
maxWidth: '100%', maxWidth: '100%',
@@ -63,11 +48,13 @@ export const BasicNavigationItem = ({
count, count,
isSmallScreen, isSmallScreen,
isExpanded, isExpanded,
onSidePanelToggle,
}: { }: {
route: Route route: Route
count?: number count?: number
isSmallScreen: boolean isSmallScreen: boolean
isExpanded: boolean isExpanded: boolean
onSidePanelToggle: (isOpen: boolean) => void
}) => { }) => {
const { pathname } = useLocation() const { pathname } = useLocation()
const href = useHref(route.path) const href = useHref(route.path)
@@ -93,7 +80,13 @@ export const BasicNavigationItem = ({
} }
return ( return (
<NavigationLink active={isActiveRoute} startAdornment={route?.icon} css={{ whiteSpace: 'nowrap' }} href={href}> <NavigationLink
onClick={isSmallScreen ? () => onSidePanelToggle(false) : undefined}
active={isActiveRoute}
startAdornment={route?.icon}
css={{ whiteSpace: 'nowrap' }}
href={href}
>
{route.label} {route.label}
{!!count && ( {!!count && (
<Badge variant={isActiveRoute ? 'green' : undefined} css={{ ml: '$2' }}> <Badge variant={isActiveRoute ? 'green' : undefined} css={{ ml: '$2' }}>
@@ -115,7 +108,7 @@ export const SideBarPanel = ({
return ( return (
<SidePanel <SidePanel
open={isOpen && windowSize.width < LAPTOP_BP} open={isOpen && windowSize.width <= LAPTOP_BP}
onOpenChange={onOpenChange} onOpenChange={onOpenChange}
side="left" side="left"
css={{ width: 264, p: 0 }} css={{ width: 264, p: 0 }}
@@ -147,8 +140,10 @@ export const SideNav = ({
const [isSmallScreen, setIsSmallScreen] = useState(false) const [isSmallScreen, setIsSmallScreen] = useState(false)
useEffect(() => { useEffect(() => {
setIsSmallScreen(isResponsive && windowSize.width < LAPTOP_BP) setIsSmallScreen(windowSize.width <= LAPTOP_BP)
}, [isExpanded, isResponsive, windowSize.width]) }, [isExpanded, windowSize.width])
const isSmallAndResponsive = useMemo(() => isSmallScreen && isResponsive, [isResponsive, isSmallScreen])
const totalValueByPath = useMemo<{ [key: string]: number }>( const totalValueByPath = useMemo<{ [key: string]: number }>(
() => ({ () => ({
@@ -166,7 +161,7 @@ export const SideNav = ({
return ( return (
<NavigationDrawer <NavigationDrawer
data-collapsed={isExpanded && isResponsive && isSmallScreen} data-collapsed={isExpanded && isSmallAndResponsive}
css={{ css={{
width: 264, width: 264,
height: '100vh', height: '100vh',
@@ -226,12 +221,11 @@ export const SideNav = ({
? { mt: '$4', px: 0, justifyContent: 'center' } ? { mt: '$4', px: 0, justifyContent: 'center' }
: undefined, : undefined,
}} }}
href="https://github.com/traefik/traefik/" href={useHref('/')}
target="_blank"
data-testid="proxy-main-nav" data-testid="proxy-main-nav"
> >
<Logo height={isSmallScreen ? 36 : 56} isSmallScreen={isSmallScreen} /> <Logo height={isSmallAndResponsive ? 36 : 56} isSmallScreen={isSmallAndResponsive} />
{!!version && !isSmallScreen && ( {!!version && !isSmallAndResponsive && (
<TooltipText text={version} css={{ maxWidth: 50, fontWeight: '$semiBold' }} isTruncated /> <TooltipText text={version} css={{ maxWidth: 50, fontWeight: '$semiBold' }} isTruncated />
)} )}
</Flex> </Flex>
@@ -270,6 +264,7 @@ export const SideNav = ({
count={totalValueByPath[item.path]} count={totalValueByPath[item.path]}
isSmallScreen={isSmallScreen} isSmallScreen={isSmallScreen}
isExpanded={isExpanded} isExpanded={isExpanded}
onSidePanelToggle={onSidePanelToggle}
/> />
))} ))}
</Flex> </Flex>
@@ -288,94 +283,13 @@ export const SideNav = ({
</NavigationLink> </NavigationLink>
</Flex> </Flex>
<ApimDemoNavMenu isResponsive={isResponsive} isSmallScreen={isSmallScreen} isExpanded={isExpanded} /> <ApimDemoNavMenu
isResponsive={isResponsive}
isSmallScreen={isSmallScreen}
isExpanded={isExpanded}
onSidePanelToggle={onSidePanelToggle}
/>
</Container> </Container>
</NavigationDrawer> </NavigationDrawer>
) )
} }
export const TopNav = ({ css, noHubButton = false }: { css?: CSS; noHubButton?: boolean }) => {
const { version } = useContext(VersionContext)
const isDarkMode = useIsDarkMode()
const parsedVersion = useMemo(() => {
if (!version) {
return 'master'
}
if (version === 'dev') {
return 'master'
}
const matches = version.match(/^(v?\d+\.\d+)/)
return matches ? 'v' + matches[1] : 'master'
}, [version])
const { signatureVerified, scriptBlobUrl, isCustomElementDefined } = useHubUpgradeButton()
const displayUpgradeToHubButton = useMemo(
() => !noHubButton && signatureVerified && (!!scriptBlobUrl || isCustomElementDefined),
[isCustomElementDefined, noHubButton, scriptBlobUrl, signatureVerified],
)
return (
<>
{displayUpgradeToHubButton && (
<Helmet>
<meta
httpEquiv="Content-Security-Policy"
content="script-src 'self' blob: 'unsafe-inline'; object-src 'none'; base-uri 'self';"
/>
<script src={scriptBlobUrl as string} type="module"></script>
</Helmet>
)}
<Flex as="nav" role="navigation" justify="end" align="center" css={{ gap: '$2', mb: '$6', ...css }}>
{displayUpgradeToHubButton && (
<Box css={{ fontFamily: '$rubik', fontWeight: '500 !important' }}>
<hub-button-app
key={`dark-mode-${isDarkMode}`}
style={{ backgroundColor: isDarkMode ? DARK_PRIMARY_COLOR : LIGHT_PRIMARY_COLOR, fontWeight: 'inherit' }}
/>
</Box>
)}
<ThemeSwitcher />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button ghost variant="secondary" css={{ px: '$2', boxShadow: 'none' }} data-testid="help-menu">
<FiHelpCircle size={20} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuPortal>
<DropdownMenuContent align="end" css={{ zIndex: 9999 }}>
<DropdownMenuGroup>
<DropdownMenuItem css={{ height: '$6', cursor: 'pointer' }}>
<Link
href={`https://doc.traefik.io/traefik/${parsedVersion}`}
target="_blank"
css={{ textDecoration: 'none', '&:hover': { textDecoration: 'none' } }}
>
<Flex align="center" gap={2}>
<FiBookOpen size={20} />
<Text>Documentation</Text>
</Flex>
</Link>
</DropdownMenuItem>
<DropdownMenuItem css={{ height: '$6', cursor: 'pointer' }}>
<Link
href="https://github.com/traefik/traefik/"
target="_blank"
css={{ textDecoration: 'none', '&:hover': { textDecoration: 'none' } }}
>
<Flex align="center" gap={2}>
<FiGithub size={20} />
<Text>Github Repository</Text>
</Flex>
</Link>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
</Flex>
</>
)
}

View File

@@ -0,0 +1,158 @@
import {
Box,
Button,
CSS,
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuTrigger,
Flex,
Link,
Text,
Tooltip,
} from '@traefiklabs/faency'
import { useContext, useEffect, useMemo, useState } from 'react'
import { FiBookOpen, FiChevronLeft, FiGithub, FiHeart, FiHelpCircle } from 'react-icons/fi'
import { useLocation } from 'react-router-dom'
import { DARK_PRIMARY_COLOR, LIGHT_PRIMARY_COLOR } from '../Page'
import ThemeSwitcher from 'components/ThemeSwitcher'
import { VersionContext } from 'contexts/version'
import { useRouterReturnTo } from 'hooks/use-href-with-return-to'
import { useIsDarkMode } from 'hooks/use-theme'
const TopNavBarBackLink = () => {
const { returnTo, returnToLabel } = useRouterReturnTo()
const { pathname } = useLocation()
if (!returnTo || pathname.includes('hub-dashboard')) return <Box />
return (
<Flex css={{ alignItems: 'center', gap: '$2' }}>
<Link href={returnTo}>
<Button as="div" ghost variant="secondary" css={{ boxShadow: 'none', p: 0, pr: '$2' }}>
<FiChevronLeft style={{ paddingRight: '4px' }} />
{returnToLabel || 'Back'}
</Button>
</Link>
</Flex>
)
}
export const TopNav = ({ css, noHubButton = false }: { css?: CSS; noHubButton?: boolean }) => {
const [hasHubButtonComponent, setHasHubButtonComponent] = useState(false)
const { showHubButton, version } = useContext(VersionContext)
const isDarkMode = useIsDarkMode()
const parsedVersion = useMemo(() => {
if (!version) {
return 'master'
}
if (version === 'dev') {
return 'master'
}
const matches = version.match(/^(v?\d+\.\d+)/)
return matches ? 'v' + matches[1] : 'master'
}, [version])
useEffect(() => {
if (!showHubButton) {
setHasHubButtonComponent(false)
return
}
if (customElements.get('hub-button-app')) {
setHasHubButtonComponent(true)
return
}
const scripts: HTMLScriptElement[] = []
const createScript = (scriptSrc: string): HTMLScriptElement => {
const script = document.createElement('script')
script.src = scriptSrc
script.async = true
script.onload = () => {
setHasHubButtonComponent(customElements.get('hub-button-app') !== undefined)
}
scripts.push(script)
return script
}
// Source: https://github.com/traefik/traefiklabs-hub-button-app
document.head.appendChild(createScript('traefiklabs-hub-button-app/main-v1.js'))
return () => {
// Remove the scripts on unmount.
scripts.forEach((script) => {
if (script.parentNode) {
script.parentNode.removeChild(script)
}
})
}
}, [showHubButton])
return (
<Flex as="nav" role="navigation" justify="space-between" align="center" css={{ mb: '$6', ...css }}>
<TopNavBarBackLink />
<Flex gap={2} align="center">
{!noHubButton && hasHubButtonComponent && (
<Box css={{ fontFamily: '$rubik', fontWeight: '500 !important' }}>
<hub-button-app
key={`dark-mode-${isDarkMode}`}
style={{ backgroundColor: isDarkMode ? DARK_PRIMARY_COLOR : LIGHT_PRIMARY_COLOR, fontWeight: 'inherit' }}
/>
</Box>
)}
<Tooltip content="Sponsor" side="bottom">
<Link href="https://github.com/sponsors/traefik" target="_blank">
<Button as="div" ghost css={{ px: '$2', boxShadow: 'none' }}>
<FiHeart size={20} color="#db61a2" />
</Button>
</Link>
</Tooltip>
<ThemeSwitcher />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button ghost variant="secondary" css={{ px: '$2', boxShadow: 'none' }} data-testid="help-menu">
<FiHelpCircle size={20} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuPortal>
<DropdownMenuContent align="end" css={{ zIndex: 9999 }}>
<DropdownMenuGroup>
<DropdownMenuItem css={{ height: '$6', cursor: 'pointer' }}>
<Link
href={`https://doc.traefik.io/traefik/${parsedVersion}`}
target="_blank"
css={{ textDecoration: 'none', '&:hover': { textDecoration: 'none' } }}
>
<Flex align="center" gap={2}>
<FiBookOpen size={20} />
<Text>Documentation</Text>
</Flex>
</Link>
</DropdownMenuItem>
<DropdownMenuItem css={{ height: '$6', cursor: 'pointer' }}>
<Link
href="https://github.com/traefik/traefik/"
target="_blank"
css={{ textDecoration: 'none', '&:hover': { textDecoration: 'none' } }}
>
<Flex align="center" gap={2}>
<FiGithub size={20} />
<Text>Github Repository</Text>
</Flex>
</Link>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
</Flex>
</Flex>
)
}

View File

@@ -0,0 +1,5 @@
// common breakpoint for large screen, cf. https://www.w3schools.com/howto/howto_css_media_query_breakpoints.asp
export const LAPTOP_BP = 1200
export * from './SideNavBar'
export * from './TopNavBar'

View File

@@ -9,7 +9,7 @@ import { PUBLIC_KEY } from './constants'
import { SpinnerLoader } from 'components/SpinnerLoader' import { SpinnerLoader } from 'components/SpinnerLoader'
import { useIsDarkMode } from 'hooks/use-theme' import { useIsDarkMode } from 'hooks/use-theme'
import { TopNav } from 'layout/Navigation' import { TopNav } from 'layout/navigation'
const SCRIPT_URL = 'https://assets.traefik.io/hub-ui-demo.js' const SCRIPT_URL = 'https://assets.traefik.io/hub-ui-demo.js'

View File

@@ -6,16 +6,18 @@ import { HubDemoContext } from './demoNavContext'
import { HubIcon } from './icons' import { HubIcon } from './icons'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { BasicNavigationItem, LAPTOP_BP } from 'layout/Navigation' import { BasicNavigationItem, LAPTOP_BP } from 'layout/navigation'
const ApimDemoNavMenu = ({ const ApimDemoNavMenu = ({
isResponsive, isResponsive,
isSmallScreen, isSmallScreen,
isExpanded, isExpanded,
onSidePanelToggle,
}: { }: {
isResponsive: boolean isResponsive: boolean
isSmallScreen: boolean isSmallScreen: boolean
isExpanded: boolean isExpanded: boolean
onSidePanelToggle: (isOpen: boolean) => void
}) => { }) => {
const [isCollapsed, setIsCollapsed] = useState(false) const [isCollapsed, setIsCollapsed] = useState(false)
const { navigationItems: hubDemoNavItems } = useContext(HubDemoContext) const { navigationItems: hubDemoNavItems } = useContext(HubDemoContext)
@@ -38,7 +40,7 @@ const ApimDemoNavMenu = ({
transition: 'transform 0.3s ease-in-out', transition: 'transform 0.3s ease-in-out',
}} }}
/> />
{isSmallScreen ? ( {isSmallScreen && isResponsive ? (
<Tooltip label="Hub demo"> <Tooltip label="Hub demo">
<Box css={{ ml: 4, color: '$navButtonText' }}> <Box css={{ ml: 4, color: '$navButtonText' }}>
<HubIcon width={20} /> <HubIcon width={20} />
@@ -74,6 +76,7 @@ const ApimDemoNavMenu = ({
route={route} route={route}
isSmallScreen={isSmallScreen} isSmallScreen={isSmallScreen}
isExpanded={isExpanded} isExpanded={isExpanded}
onSidePanelToggle={onSidePanelToggle}
/> />
))} ))}
</Box> </Box>