Repository: traefik/traefik (language: go)

Issue:
Activate tracing by routers
### Welcome!

- [X] Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
- [X] Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.

### What did you expect to see?

It would be very convenient to be able to activate the Tracing only for one or more routers. 
We used a mutualized traefik with a lot of services and routers, it will be lighter to enable the tracing but deactivate it by default on all the routers/services and then in the router/services definition (dynamic config) enable it.
I know it  we can filter in the tracing collector but it's better to reduce at the source.



Discussion / hints:
Hey @StephaneG31!

Thanks for your suggestion.

We are interested in this issue. 
We are going to leave the status as kind/proposal to give the community time to let us know if they would like this idea.
We will reevaluate as people respond.

Conversation is time-boxed to 6 months.

Hey StephaneG31, thanks for you proposal :)
Enabling, on demand, per service, tracing & access logs could be interesting.
We would love to discuss different options to implement this.
Hello @StephaneG31,

This would not make it to our roadmap as we are focused elsewhere. If a community member would like to build it, let us know, and we will work with you to ensure you have all the information needed to merge it.

We prefer to work with our community members at the beginning of the design process to ensure that we are aligned and can move quickly with the review and merge process. Let us know here or create a PR before you start, and we will work with you there.

Don’t forget to check out the[ contributor docs](https://github.com/traefik/contributors-guide/blob/master/pr_guidelines.md) and link the PR to this issue.

Fix patch:
diff --git a/docs/content/observability/metrics/datadog.md b/docs/content/observability/metrics/datadog.md
index 61c64f4c90..fa3ac4592a 100644
--- a/docs/content/observability/metrics/datadog.md
+++ b/docs/content/observability/metrics/datadog.md
@@ -68,6 +68,7 @@ metrics:
 ```bash tab="CLI"
 --metrics.datadog.addEntryPointsLabels=true
 ```
+
 #### `addRoutersLabels`
 
 _Optional, Default=false_
diff --git a/docs/content/observability/overview.md b/docs/content/observability/overview.md
index f5f46bbf3b..bcd4385dd2 100644
--- a/docs/content/observability/overview.md
+++ b/docs/content/observability/overview.md
@@ -5,16 +5,80 @@ description: "Traefik provides Logs, Access Logs, Metrics and Tracing. Read the
 
 # Overview
 
-Traefik's Observability system
-{: .subtitle }
+Traefik’s observability features include logs, access logs, metrics, and tracing. You can configure these options globally or at more specific levels, such as per router or per entry point.
 
-## Logs
+## Configuration Example
+
+Enable access logs, metrics, and tracing globally
+
+```yaml tab="File (YAML)"
+accessLog: {}
+
+metrics:
+  otlp: {}
+
+tracing: {}
+```
+
+```yaml tab="File (TOML)"
+[accessLog]
+
+[metrics]
+  [metrics.otlp]
+
+[tracing]
+```
+
+```bash tab="CLI"
+--accesslog=true
+--metrics.otlp=true
+--tracing=true
+```
+
+You can disable access logs, metrics, and tracing for a specific entrypoint attached to a router:
+
+```yaml tab="File (YAML)"
+# Static Configuration
+entryPoints:
+  EntryPoint0:
+    address: ':8000/udp'
+    observability:
+      accessLogs: false
+      tracing: false
+      metrics: false
+```
+
+```toml tab="File (TOML)"
+# Static Configuration
+[entryPoints.EntryPoint0]
+  address = ":8000/udp"
+
+    [entryPoints.EntryPoint0.observability]
+      accessLogs = false
+      tracing = false
+      metrics = false
+```
+
+```bash tab="CLI"
+# Static Configuration
+--entryPoints.EntryPoint0.address=:8000/udp
+--entryPoints.EntryPoint0.observability.accessLogs=false
+--entryPoints.EntryPoint0.observability.metrics=false
+--entryPoints.EntryPoint0.observability.tracing=false
+```
+
+!!!note "Default Behavior"
+    A router with its own observability configuration will override the global default.
+
+## Configuration Options
+
+### Logs
 
 Traefik logs informs about everything that happens within Traefik (startup, configuration, events, shutdown, and so on).
 
 Read the [Logs documentation](./logs.md) to learn how to configure it.
 
-## Access Logs
+### Access Logs
 
 Access logs are a key part of observability in Traefik.
 
@@ -24,7 +88,7 @@ including the source IP address, requested URL, response status code, and more.
 
 Read the [Access Logs documentation](./access-logs.md) to learn how to configure it.
 
-## Metrics
+### Metrics
 
 Traefik offers a metrics feature that provides valuable insights about the performance and usage.
 These metrics include the number of requests received, the requests duration, and more.
@@ -33,7 +97,7 @@ On top of supporting metrics in the OpenTelemetry format, Traefik supports the f
 
 Read the [Metrics documentation](./metrics/overview.md) to learn how to configure it.
 
-## Tracing
+### Tracing
 
 The Traefik tracing system allows developers to gain deep visibility into the flow of requests through their infrastructure.
 
diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml
index b126ab96c1..63b86f2f0f 100644
--- a/docs/content/reference/dynamic-configuration/docker-labels.yml
+++ b/docs/content/reference/dynamic-configuration/docker-labels.yml
@@ -147,6 +147,9 @@
 - "traefik.http.middlewares.middleware25.stripprefixregex.regex=foobar, foobar"
 - "traefik.http.routers.router0.entrypoints=foobar, foobar"
 - "traefik.http.routers.router0.middlewares=foobar, foobar"
+- "traefik.http.routers.router0.observability.accesslogs=true"
+- "traefik.http.routers.router0.observability.metrics=true"
+- "traefik.http.routers.router0.observability.tracing=true"
 - "traefik.http.routers.router0.priority=42"
 - "traefik.http.routers.router0.rule=foobar"
 - "traefik.http.routers.router0.rulesyntax=foobar"
@@ -160,6 +163,9 @@
 - "traefik.http.routers.router0.tls.options=foobar"
 - "traefik.http.routers.router1.entrypoints=foobar, foobar"
 - "traefik.http.routers.router1.middlewares=foobar, foobar"
+- "traefik.http.routers.router1.observability.accesslogs=true"
+- "traefik.http.routers.router1.observability.metrics=true"
+- "traefik.http.routers.router1.observability.tracing=true"
 - "traefik.http.routers.router1.priority=42"
 - "traefik.http.routers.router1.rule=foobar"
 - "traefik.http.routers.router1.rulesyntax=foobar"
diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml
index e1a93d65f2..0a0c6ec25b 100644
--- a/docs/content/reference/dynamic-configuration/file.toml
+++ b/docs/content/reference/dynamic-configuration/file.toml
@@ -20,6 +20,10 @@
         [[http.routers.Router0.tls.domains]]
           main = "foobar"
           sans = ["foobar", "foobar"]
+      [http.routers.Router0.observability]
+        accessLogs = true
+        tracing = true
+        metrics = true
     [http.routers.Router1]
       entryPoints = ["foobar", "foobar"]
       middlewares = ["foobar", "foobar"]
@@ -38,6 +42,10 @@
         [[http.routers.Router1.tls.domains]]
           main = "foobar"
           sans = ["foobar", "foobar"]
+      [http.routers.Router1.observability]
+        accessLogs = true
+        tracing = true
+        metrics = true
   [http.services]
     [http.services.Service01]
       [http.services.Service01.failover]
diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml
index 4f2ae185d9..05908bb184 100644
--- a/docs/content/reference/dynamic-configuration/file.yaml
+++ b/docs/content/reference/dynamic-configuration/file.yaml
@@ -25,6 +25,10 @@ http:
             sans:
               - foobar
               - foobar
+      observability:
+        accessLogs: true
+        tracing: true
+        metrics: true
     Router1:
       entryPoints:
         - foobar
@@ -48,6 +52,10 @@ http:
             sans:
               - foobar
               - foobar
+      observability:
+        accessLogs: true
+        tracing: true
+        metrics: true
   services:
     Service01:
       failover:
diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
index 8ba8377e1b..86ccec1736 100644
--- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
+++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
@@ -86,6 +86,18 @@ spec:
                         - name
                         type: object
                       type: array
+                    observability:
+                      description: |-
+                        Observability defines the observability configuration for a router.
+                        More info: https://doc.traefik.io/traefik/v3.2/routing/routers/#observability
+                      properties:
+                        accessLogs:
+                          type: boolean
+                        metrics:
+                          type: boolean
+                        tracing:
+                          type: boolean
+                      type: object
                     priority:
                       description: |-
                         Priority defines the router's priority.
diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md
index 21b23ecbef..46d27bea8f 100644
--- a/docs/content/reference/dynamic-configuration/kv-ref.md
+++ b/docs/content/reference/dynamic-configuration/kv-ref.md
@@ -173,6 +173,9 @@ THIS FILE MUST NOT BE EDITED BY HAND
 | `traefik/http/routers/Router0/entryPoints/1` | `foobar` |
 | `traefik/http/routers/Router0/middlewares/0` | `foobar` |
 | `traefik/http/routers/Router0/middlewares/1` | `foobar` |
+| `traefik/http/routers/Router0/observability/accessLogs` | `true` |
+| `traefik/http/routers/Router0/observability/metrics` | `true` |
+| `traefik/http/routers/Router0/observability/tracing` | `true` |
 | `traefik/http/routers/Router0/priority` | `42` |
 | `traefik/http/routers/Router0/rule` | `foobar` |
 | `traefik/http/routers/Router0/ruleSyntax` | `foobar` |
@@ -189,6 +192,9 @@ THIS FILE MUST NOT BE EDITED BY HAND
 | `traefik/http/routers/Router1/entryPoints/1` | `foobar` |
 | `traefik/http/routers/Router1/middlewares/0` | `foobar` |
 | `traefik/http/routers/Router1/middlewares/1` | `foobar` |
+| `traefik/http/routers/Router1/observability/accessLogs` | `true` |
+| `traefik/http/routers/Router1/observability/metrics` | `true` |
+| `traefik/http/routers/Router1/observability/tracing` | `true` |
 | `traefik/http/routers/Router1/priority` | `42` |
 | `traefik/http/routers/Router1/rule` | `foobar` |
 | `traefik/http/routers/Router1/ruleSyntax` | `foobar` |
diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml
index 07a5ead8be..d0f042d91b 100644
--- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml
+++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml
@@ -86,6 +86,18 @@ spec:
                         - name
                         type: object
                       type: array
+                    observability:
+                      description: |-
+                        Observability defines the observability configuration for a router.
+                        More info: https://doc.traefik.io/traefik/v3.2/routing/routers/#observability
+                      properties:
+                        accessLogs:
+                          type: boolean
+                        metrics:
+                          type: boolean
+                        tracing:
+                          type: boolean
+                      type: object
                     priority:
                       description: |-
                         Priority defines the router's priority.
diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md
index 0c65188fe8..7feb191204 100644
--- a/docs/content/reference/static-configuration/cli-ref.md
+++ b/docs/content/reference/static-configuration/cli-ref.md
@@ -264,6 +264,15 @@ HTTP/3 configuration. (Default: ```false```)
 `--entrypoints.<name>.http3.advertisedport`:  
 UDP port to advertise, on which HTTP/3 is available. (Default: ```0```)
 
+`--entrypoints.<name>.observability.accesslogs`:  
+ (Default: ```true```)
+
+`--entrypoints.<name>.observability.metrics`:  
+ (Default: ```true```)
+
+`--entrypoints.<name>.observability.tracing`:  
+ (Default: ```true```)
+
 `--entrypoints.<name>.proxyprotocol`:  
 Proxy-Protocol configuration. (Default: ```false```)
 
diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md
index 056f9b29f7..5a82b515e9 100644
--- a/docs/content/reference/static-configuration/env-ref.md
+++ b/docs/content/reference/static-configuration/env-ref.md
@@ -264,6 +264,15 @@ Subject alternative names.
 `TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_TLS_OPTIONS`:  
 Default TLS options for the routers linked to the entry point.
 
+`TRAEFIK_ENTRYPOINTS_<NAME>_OBSERVABILITY_ACCESSLOGS`:  
+ (Default: ```true```)
+
+`TRAEFIK_ENTRYPOINTS_<NAME>_OBSERVABILITY_METRICS`:  
+ (Default: ```true```)
+
+`TRAEFIK_ENTRYPOINTS_<NAME>_OBSERVABILITY_TRACING`:  
+ (Default: ```true```)
+
 `TRAEFIK_ENTRYPOINTS_<NAME>_PROXYPROTOCOL`:  
 Proxy-Protocol configuration. (Default: ```false```)
 
diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml
index 1987627ecb..25653d0ce7 100644
--- a/docs/content/reference/static-configuration/file.toml
+++ b/docs/content/reference/static-configuration/file.toml
@@ -77,6 +77,10 @@
       advertisedPort = 42
     [entryPoints.EntryPoint0.udp]
       timeout = "42s"
+    [entryPoints.EntryPoint0.observability]
+      accessLogs = true
+      tracing = true
+      metrics = true
 
 [providers]
   providersThrottleDuration = "42s"
diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml
index 2bebf70177..d40decc227 100644
--- a/docs/content/reference/static-configuration/file.yaml
+++ b/docs/content/reference/static-configuration/file.yaml
@@ -91,6 +91,10 @@ entryPoints:
       advertisedPort: 42
     udp:
       timeout: 42s
+    observability:
+      accessLogs: true
+      tracing: true
+      metrics: true
 providers:
   providersThrottleDuration: 42s
   docker:
diff --git a/docs/content/routing/entrypoints.md b/docs/content/routing/entrypoints.md
index ce459375da..aefb7ac474 100644
--- a/docs/content/routing/entrypoints.md
+++ b/docs/content/routing/entrypoints.md
@@ -1237,8 +1237,6 @@ entryPoints:
 --entryPoints.foo.udp.timeout=10s
 ```
 
-{!traefik-for-business-applications.md!}
-
 ## Systemd Socket Activation
 
 Traefik supports [systemd socket activation](https://www.freedesktop.org/software/systemd/man/latest/systemd-socket-activate.html).
@@ -1260,3 +1258,105 @@ systemd-socket-activate -l 80 -l 443 --fdname web:websecure  ./traefik --entrypo
 !!! warning "Docker Support"
 
     Socket activation is not supported by Docker but works with Podman containers.
+
+## Observability Options
+
+This section is dedicated to options to control observability for an EntryPoint.
+
+!!! info "Note that you must first enable access-logs, tracing, and/or metrics."
+
+!!! warning "AddInternals option"
+
+    By default, and for any type of signals (access-logs, metrics and tracing),
+    Traefik disables observability for internal resources.
+    The observability options described below cannot interfere with the `AddInternals` ones,
+    and will be ignored.
+
+    For instance, if a router exposes the `api@internal` service and `metrics.AddInternals` is false,
+    it will never produces metrics, even if the EntryPoint observability configuration enables metrics.
+
+### AccessLogs
+
+_Optional, Default=true_
+
+AccessLogs defines whether a router attached to this EntryPoint produces access-logs by default.
+Nonetheless, a router defining its own observability configuration will opt-out from this default.
+
+```yaml tab="File (YAML)"
+entryPoints:
+  foo:
+    address: ':8000/udp'
+    observability:
+      accessLogs: false
+```
+
+```toml tab="File (TOML)"
+[entryPoints.foo]
+  address = ":8000/udp"
+
+    [entryPoints.foo.observability]
+      accessLogs = false
+```
+
+```bash tab="CLI"
+--entryPoints.foo.address=:8000/udp
+--entryPoints.foo.observability.accessLogs=false
+```
+
+### Metrics
+
+_Optional, Default=true_
+
+Metrics defines whether a router attached to this EntryPoint produces metrics by default.
+Nonetheless, a router defining its own observability configuration will opt-out from this default.
+
+```yaml tab="File (YAML)"
+entryPoints:
+  foo:
+    address: ':8000/udp'
+    observability:
+      metrics: false
+```
+
+```toml tab="File (TOML)"
+[entryPoints.foo]
+  address = ":8000/udp"
+
+    [entryPoints.foo.observability]
+      metrics = false
+```
+
+```bash tab="CLI"
+--entryPoints.foo.address=:8000/udp
+--entryPoints.foo.observability.metrics=false
+```
+
+### Tracing
+
+_Optional, Default=true_
+
+Tracing defines whether a router attached to this EntryPoint produces traces by default.
+Nonetheless, a router defining its own observability configuration will opt-out from this default.
+
+```yaml tab="File (YAML)"
+entryPoints:
+  foo:
+    address: ':8000/udp'
+    observability:
+      tracing: false
+```
+
+```toml tab="File (TOML)"
+[entryPoints.foo]
+  address = ":8000/udp"
+
+    [entryPoints.foo.observability]
+      tracing = false
+```
+
+```bash tab="CLI"
+--entryPoints.foo.address=:8000/udp
+--entryPoints.foo.observability.tracing=false
+```
+
+{!traefik-for-business-applications.md!}
diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md
index a2b0a78783..b3c9188926 100644
--- a/docs/content/routing/providers/consul-catalog.md
+++ b/docs/content/routing/providers/consul-catalog.md
@@ -111,6 +111,30 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m
     traefik.http.routers.myrouter.tls.options=foobar
     ```
 
+??? info "`traefik.http.routers.<router_name>.observability.accesslogs`"
+
+    See accesslogs [option](../routers/index.md#accesslogs) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.accesslogs=true
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.metrics`"
+
+    See metrics [option](../routers/index.md#metrics) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.metrics=true
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.tracing`"
+
+    See tracing [option](../routers/index.md#tracing) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.tracing=true
+    ```
+
 ??? info "`traefik.http.routers.<router_name>.priority`"
 
     See [priority](../routers/index.md#priority) for more information.
diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md
index 84c3af25d0..43d7850100 100644
--- a/docs/content/routing/providers/docker.md
+++ b/docs/content/routing/providers/docker.md
@@ -224,6 +224,30 @@ For example, to change the rule, you could add the label ```traefik.http.routers
     - "traefik.http.routers.myrouter.tls.options=foobar"
     ```
 
+??? info "`traefik.http.routers.<router_name>.observability.accesslogs`"
+
+    See accesslogs [option](../routers/index.md#accesslogs) for more information.
+    
+    ```yaml
+    - "traefik.http.routers.myrouter.observability.accesslogs=true"
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.metrics`"
+
+    See metrics [option](../routers/index.md#metrics) for more information.
+    
+    ```yaml
+    - "traefik.http.routers.myrouter.observability.metrics=true"
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.tracing`"
+
+    See tracing [option](../routers/index.md#tracing) for more information.
+    
+    ```yaml
+    - "traefik.http.routers.myrouter.observability.tracing=true"
+    ```
+
 ??? info "`traefik.http.routers.<router_name>.priority`"
 
     See [priority](../routers/index.md#priority) for more information.
diff --git a/docs/content/routing/providers/ecs.md b/docs/content/routing/providers/ecs.md
index 30c82fc9bb..35ba6e09c8 100644
--- a/docs/content/routing/providers/ecs.md
+++ b/docs/content/routing/providers/ecs.md
@@ -111,6 +111,30 @@ For example, to change the rule, you could add the label ```traefik.http.routers
     traefik.http.routers.myrouter.tls.options=foobar
     ```
 
+??? info "`traefik.http.routers.<router_name>.observability.accesslogs`"
+
+    See accesslogs [option](../routers/index.md#accesslogs) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.accesslogs=true
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.metrics`"
+
+    See metrics [option](../routers/index.md#metrics) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.metrics=true
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.tracing`"
+
+    See tracing [option](../routers/index.md#tracing) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.tracing=true
+    ```
+
 ??? info "`traefik.http.routers.<router_name>.priority`"
 
     See [priority](../routers/index.md#priority) for more information.
diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md
index edd3e4a3be..85dddccae6 100644
--- a/docs/content/routing/providers/kubernetes-crd.md
+++ b/docs/content/routing/providers/kubernetes-crd.md
@@ -332,17 +332,21 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne
         middlewares:                    # [5]
         - name: middleware1             # [6]
           namespace: default            # [7]
-        services:                       # [8]
+        observability:                  # [8]
+          accesslogs: true              # [9]    
+          metrics: true                 # [10]
+          tracing: true                 # [11]
+        services:                       # [12]
         - kind: Service
           name: foo
           namespace: default
           passHostHeader: true
-          port: 80                      # [9]
+          port: 80                      # [13]
           responseForwarding:
             flushInterval: 1ms
           scheme: https
-          serversTransport: transport   # [10]
-          healthCheck:                  # [11]
+          serversTransport: transport   # [14]
+          healthCheck:                  # [15]
             path: /health
             interval: 15s
           sticky:
@@ -355,17 +359,17 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne
               path: /foo
           strategy: RoundRobin
           weight: 10
-          nativeLB: true                # [12]
-          nodePortLB: true              # [13]
-      tls:                              # [14]
-        secretName: supersecret         # [15]
-        options:                        # [16]
-          name: opt                     # [17]
-          namespace: default            # [18]
-        certResolver: foo               # [19]
-        domains:                        # [20]
-        - main: example.net             # [21]
-          sans:                         # [22]
+          nativeLB: true                # [16]
+          nodePortLB: true              # [17]
+      tls:                              # [18]
+        secretName: supersecret         # [19]
+        options:                        # [20]
+          name: opt                     # [21]
+          namespace: default            # [22]
+        certResolver: foo               # [23]
+        domains:                        # [24]
+        - main: example.net             # [25]
+          sans:                         # [26]
           - a.example.net
           - b.example.net
     ```
@@ -379,21 +383,25 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne
 | [5]  | `routes[n].middlewares`        | List of reference to [Middleware](#kind-middleware)                                                                                                                                                                                                                                          |
 | [6]  | `middlewares[n].name`          | Defines the [Middleware](#kind-middleware) name                                                                                                                                                                                                                                              |
 | [7]  | `middlewares[n].namespace`     | Defines the [Middleware](#kind-middleware) namespace. It can be omitted when the Middleware is in the IngressRoute namespace.                                                                                                                                                                |
-| [8]  | `routes[n].services`           | List of any combination of [TraefikService](#kind-traefikservice) and reference to a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) (See below for `ExternalName Service` setup)                                                                     |
-| [9]  | `services[n].port`             | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port.                                                                                                                                       |
-| [10] | `services[n].serversTransport` | Defines the reference to a [ServersTransport](#kind-serverstransport). The ServersTransport namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace (see [ServersTransport reference](#serverstransport-reference)). |
-| [11] | `services[n].healthCheck`      | Defines the HealthCheck when service references a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.                                                                                                                               |
-| [12] | `services[n].nativeLB`         | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP.                                                                                                                                     |
-| [13] | `services[n].nodePortLB`       | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort.                                                                                                                               |
-| [14] | `tls`                          | Defines [TLS](../routers/index.md#tls) certificate configuration                                                                                                                                                                                                                             |
-| [15] | `tls.secretName`               | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace)                                                                                                                                         |
-| [16] | `tls.options`                  | Defines the reference to a [TLSOption](#kind-tlsoption)                                                                                                                                                                                                                                      |
-| [17] | `options.name`                 | Defines the [TLSOption](#kind-tlsoption) name                                                                                                                                                                                                                                                |
-| [18] | `options.namespace`            | Defines the [TLSOption](#kind-tlsoption) namespace                                                                                                                                                                                                                                           |
-| [19] | `tls.certResolver`             | Defines the reference to a [CertResolver](../routers/index.md#certresolver)                                                                                                                                                                                                                  |
-| [20] | `tls.domains`                  | List of [domains](../routers/index.md#domains)                                                                                                                                                                                                                                               |
-| [21] | `domains[n].main`              | Defines the main domain name                                                                                                                                                                                                                                                                 |
-| [22] | `domains[n].sans`              | List of SANs (alternative domains)                                                                                                                                                                                                                                                           |
+| [8]  | `routes[n].observability`      | Defines the route observability configuration.                                                                                                                                                                                                                                               |
+| [9]  | `observability.accesslogs`     | Defines whether the route will produce [access-logs](../routers/index.md#accesslogs).                                                                                                                                                                                                        |
+| [10] | `observability.metrics`        | Defines whether the route will produce [metrics](../routers/index.md#metrics).                                                                                                                                                                                                               |
+| [11] | `observability.tracing`        | Defines whether the route will produce [traces](../routers/index.md#tracing).                                                                                                                                                                                                                |
+| [12] | `routes[n].services`           | List of any combination of [TraefikService](#kind-traefikservice) and reference to a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) (See below for `ExternalName Service` setup)                                                                     |
+| [13] | `services[n].port`             | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port.                                                                                                                                       |
+| [14] | `services[n].serversTransport` | Defines the reference to a [ServersTransport](#kind-serverstransport). The ServersTransport namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace (see [ServersTransport reference](#serverstransport-reference)). |
+| [15] | `services[n].healthCheck`      | Defines the HealthCheck when service references a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.                                                                                                                               |
+| [16] | `services[n].nativeLB`         | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP.                                                                                                                                     |
+| [17] | `services[n].nodePortLB`       | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort.                                                                                                                               |
+| [18] | `tls`                          | Defines [TLS](../routers/index.md#tls) certificate configuration                                                                                                                                                                                                                             |
+| [19] | `tls.secretName`               | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace)                                                                                                                                         |
+| [20] | `tls.options`                  | Defines the reference to a [TLSOption](#kind-tlsoption)                                                                                                                                                                                                                                      |
+| [21] | `options.name`                 | Defines the [TLSOption](#kind-tlsoption) name                                                                                                                                                                                                                                                |
+| [22] | `options.namespace`            | Defines the [TLSOption](#kind-tlsoption) namespace                                                                                                                                                                                                                                           |
+| [23] | `tls.certResolver`             | Defines the reference to a [CertResolver](../routers/index.md#certresolver)                                                                                                                                                                                                                  |
+| [24] | `tls.domains`                  | List of [domains](../routers/index.md#domains)                                                                                                                                                                                                                                               |
+| [25] | `domains[n].main`              | Defines the main domain name                                                                                                                                                                                                                                                                 |
+| [26] | `domains[n].sans`              | List of SANs (alternative domains)                                                                                                                                                                                                                                                           |
 
 ??? example "Declaring an IngressRoute"
 
diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md
index 39993b60a9..b8a4eaf9ac 100644
--- a/docs/content/routing/providers/kubernetes-ingress.md
+++ b/docs/content/routing/providers/kubernetes-ingress.md
@@ -288,6 +288,30 @@ which in turn will create the resulting routers, services, handlers, etc.
     traefik.ingress.kubernetes.io/router.tls.options: foobar@file
     ```
 
+??? info "`traefik.ingress.kubernetes.io/router.observability.accesslogs`"
+
+    See accesslogs [option](../routers/index.md#accesslogs) for more information.
+
+    ```yaml
+    traefik.ingress.kubernetes.io/router.observability.accesslogs: true
+    ```
+
+??? info "`traefik.ingress.kubernetes.io/router.observability.metrics`"
+
+    See metrics [option](../routers/index.md#metrics) for more information.
+
+    ```yaml
+    traefik.ingress.kubernetes.io/router.observability.metrics: true
+    ```
+
+??? info "`traefik.ingress.kubernetes.io/router.observability.tracing`"
+
+    See tracing [option](../routers/index.md#tracing) for more information.
+
+    ```yaml
+    traefik.ingress.kubernetes.io/router.observability.tracing: true
+    ```
+
 #### On Service
 
 ??? info "`traefik.ingress.kubernetes.io/service.nativelb`"
diff --git a/docs/content/routing/providers/kv.md b/docs/content/routing/providers/kv.md
index ba440db3f4..86f70bc148 100644
--- a/docs/content/routing/providers/kv.md
+++ b/docs/content/routing/providers/kv.md
@@ -95,6 +95,30 @@ A Story of key & values
     |---------------------------------------------|----------|
     | `traefik/http/routers/myrouter/tls/options` | `foobar` |
 
+??? info "`traefik/http/routers/<router_name>/observability/accesslogs`"
+
+    See accesslogs [option](../routers/index.md#accesslogs) for more information.
+
+    | Key (Path)                                               | Value  |
+    |----------------------------------------------------------|--------|
+    | `traefik/http/routers/myrouter/observability/accesslogs` | `true` |
+
+??? info "`traefik/http/routers/<router_name>/observability/metrics`"
+
+    See metrics [option](../routers/index.md#metrics) for more information.
+
+    | Key (Path)                                            | Value  |
+    |-------------------------------------------------------|--------|
+    | `traefik/http/routers/myrouter/observability/metrics` | `true` |
+
+??? info "`traefik/http/routers/<router_name>/observability/tracing`"
+
+    See tracing [option](../routers/index.md#tracing) for more information.
+
+    | Key (Path)                                            | Value  |
+    |-------------------------------------------------------|--------|
+    | `traefik/http/routers/myrouter/observability/tracing` | `true` |
+
 ??? info "`traefik/http/routers/<router_name>/priority`"
 
     See [priority](../routers/index.md#priority) for more information.
diff --git a/docs/content/routing/providers/marathon.md b/docs/content/routing/providers/marathon.md
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/docs/content/routing/providers/nomad.md b/docs/content/routing/providers/nomad.md
index 2fbdd8a4ea..4760912011 100644
--- a/docs/content/routing/providers/nomad.md
+++ b/docs/content/routing/providers/nomad.md
@@ -111,6 +111,30 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m
     traefik.http.routers.myrouter.tls.options=foobar
     ```
 
+??? info "`traefik.http.routers.<router_name>.observability.accesslogs`"
+
+    See accesslogs [option](../routers/index.md#accesslogs) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.accesslogs=true
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.metrics`"
+
+    See metrics [option](../routers/index.md#metrics) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.metrics=true
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.tracing`"
+
+    See tracing [option](../routers/index.md#tracing) for more information.
+    
+    ```yaml
+    traefik.http.routers.myrouter.observability.tracing=true
+    ```
+
 ??? info "`traefik.http.routers.<router_name>.priority`"
 
     See [priority](../routers/index.md#priority) for more information.
diff --git a/docs/content/routing/providers/service-by-label.md b/docs/content/routing/providers/service-by-label.md
index 611f1ad2e4..47da395380 100644
--- a/docs/content/routing/providers/service-by-label.md
+++ b/docs/content/routing/providers/service-by-label.md
@@ -7,7 +7,8 @@ There are, however, exceptions when using label-based configurations:
 and a label defines a service (e.g. implicitly through a loadbalancer server port value),
 but the router does not specify any service,
 then that service is automatically assigned to the router.
-1. If a label defines a router (e.g. through a router Rule) but no service is defined,
+
+2. If a label defines a router (e.g. through a router Rule) but no service is defined,
 then a service is automatically created and assigned to the router.
 
 !!! info ""
diff --git a/docs/content/routing/providers/swarm.md b/docs/content/routing/providers/swarm.md
index e6968b9175..39c46235fa 100644
--- a/docs/content/routing/providers/swarm.md
+++ b/docs/content/routing/providers/swarm.md
@@ -235,6 +235,30 @@ For example, to change the rule, you could add the label ```traefik.http.routers
     - "traefik.http.routers.myrouter.tls.options=foobar"
     ```
 
+??? info "`traefik.http.routers.<router_name>.observability.accesslogs`"
+
+    See accesslogs [option](../routers/index.md#accesslogs) for more information.
+    
+    ```yaml
+    - "traefik.http.routers.myrouter.observability.accesslogs=true"
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.metrics`"
+
+    See metrics [option](../routers/index.md#metrics) for more information.
+    
+    ```yaml
+    - "traefik.http.routers.myrouter.observability.metrics=true"
+    ```
+
+??? info "`traefik.http.routers.<router_name>.observability.tracing`"
+
+    See tracing [option](../routers/index.md#tracing) for more information.
+    
+    ```yaml
+    - "traefik.http.routers.myrouter.observability.tracing=true"
+    ```
+
 ??? info "`traefik.http.routers.<router_name>.priority`"
 
     See [priority](../routers/index.md#priority) for more information.
diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md
index 7e93c79c33..ef61d56b1d 100644
--- a/docs/content/routing/routers/index.md
+++ b/docs/content/routing/routers/index.md
@@ -877,6 +877,117 @@ The [supported `provider` table](../../https/acme.md#providers) indicates if the
 !!! warning "Double Wildcard Certificates"
     It is not possible to request a double wildcard certificate for a domain (for example `*.*.local.com`).
 
+### Observability
+
+The Observability section defines a per router behavior regarding access-logs, metrics or tracing.
+
+The default router observability configuration is inherited from the attached EntryPoints and can be configured with the observability [options](../../routing/entrypoints.md#observability-options).
+However, a router defining its own observability configuration will opt-out from these defaults.
+
+!!! info "Note that to enable router-level observability, you must first enable access-logs, tracing, and/or metrics."
+    
+!!! warning "AddInternals option"
+
+    By default, and for any type of signals (access-logs, metrics and tracing),
+    Traefik disables observability for internal resources.
+    The observability options described below cannot interfere with the `AddInternals` ones,
+    and will be ignored.
+
+    For instance, if a router exposes the `api@internal` service and `metrics.AddInternals` is false,
+    it will never produces metrics, even if the router observability configuration enables metrics.
+
+#### `accessLogs`
+
+_Optional_
+
+The `accessLogs` option controls whether the router will produce access-logs.
+
+??? example "Disable access-logs for a router using the [File Provider](../../providers/file.md)"
+
+    ```yaml tab="YAML"
+    ## Dynamic configuration
+    http:
+      routers:
+        my-router:
+          rule: "Path(`/foo`)"
+          service: service-foo
+          observability:
+            accessLogs: false
+    ```
+
+    ```toml tab="TOML"
+    ## Dynamic configuration
+    [http.routers]
+      [http.routers.my-router]
+        rule = "Path(`/foo`)"
+        service = "service-foo"
+        [http.routers.my-router.observability]
+          accessLogs = false
+    ```
+
+#### `metrics`
+
+_Optional_
+
+The `metrics` option controls whether the router will produce metrics.
+
+!!! warning "Metrics layers"
+
+    When metrics layers are not enabled with the `addEntryPointsLabels`, `addRoutersLabels` and/or `addServicesLabels` options,
+    enabling metrics for a router will not enable them.
+
+??? example "Disable metrics for a router using the [File Provider](../../providers/file.md)"
+
+    ```yaml tab="YAML"
+    ## Dynamic configuration
+    http:
+      routers:
+        my-router:
+          rule: "Path(`/foo`)"
+          service: service-foo
+          observability:
+            metrics: false
+    ```
+
+    ```toml tab="TOML"
+    ## Dynamic configuration
+    [http.routers]
+      [http.routers.my-router]
+        rule = "Path(`/foo`)"
+        service = "service-foo"
+        [http.routers.my-router.observability]
+          metrics = false
+    ```
+
+#### `tracing`
+
+_Optional_
+
+The `tracing` option controls whether the router will produce traces.
+
+??? example "Disable tracing for a router using the [File Provider](../../providers/file.md)"
+
+    ```yaml tab="YAML"
+    ## Dynamic configuration
+    http:
+      routers:
+        my-router:
+          rule: "Path(`/foo`)"
+          service: service-foo
+          observability:
+            tracing: false
+    ```
+
+    ```toml tab="TOML"
+    ## Dynamic configuration
+    [http.routers]
+      [http.routers.my-router]
+        rule = "Path(`/foo`)"
+        service = "service-foo"
+        [http.routers.my-router.observability]
+          tracing = false
+    ```
+
 ## Configuring TCP Routers
 
 !!! warning "The character `@` is not authorized in the router name"
diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml
index 8ba8377e1b..86ccec1736 100644
--- a/integration/fixtures/k8s/01-traefik-crd.yml
+++ b/integration/fixtures/k8s/01-traefik-crd.yml
@@ -86,6 +86,18 @@ spec:
                         - name
                         type: object
                       type: array
+                    observability:
+                      description: |-
+                        Observability defines the observability configuration for a router.
+                        More info: https://doc.traefik.io/traefik/v3.2/routing/routers/#observability
+                      properties:
+                        accessLogs:
+                          type: boolean
+                        metrics:
+                          type: boolean
+                        tracing:
+                          type: boolean
+                      type: object
                     priority:
                       description: |-
                         Priority defines the router's priority.
diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go
index 7655036b0a..18552294db 100644
--- a/pkg/config/dynamic/http_config.go
+++ b/pkg/config/dynamic/http_config.go
@@ -36,11 +36,12 @@ type HTTPConfiguration struct {
 
 // +k8s:deepcopy-gen=true
 
-// Model is a set of default router's values.
+// Model holds model configuration.
 type Model struct {
-	Middlewares       []string         `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
-	TLS               *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
-	DefaultRuleSyntax string           `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
+	Middlewares       []string                  `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
+	TLS               *RouterTLSConfig          `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
+	Observability     RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"`
+	DefaultRuleSyntax string                    `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
 }
 
 // +k8s:deepcopy-gen=true
@@ -57,14 +58,15 @@ type Service struct {
 
 // Router holds the router configuration.
 type Router struct {
-	EntryPoints []string         `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
-	Middlewares []string         `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
-	Service     string           `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
-	Rule        string           `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
-	RuleSyntax  string           `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
-	Priority    int              `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
-	TLS         *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
-	DefaultRule bool             `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
+	EntryPoints   []string                   `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
+	Middlewares   []string                   `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
+	Service       string                     `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
+	Rule          string                     `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
+	RuleSyntax    string                     `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
+	Priority      int                        `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
+	TLS           *RouterTLSConfig           `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
+	Observability *RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"`
+	DefaultRule   bool                       `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
 }
 
 // +k8s:deepcopy-gen=true
@@ -78,6 +80,15 @@ type RouterTLSConfig struct {
 
 // +k8s:deepcopy-gen=true
 
+// RouterObservabilityConfig holds the observability configuration for a router.
+type RouterObservabilityConfig struct {
+	AccessLogs *bool `json:"accessLogs,omitempty" toml:"accessLogs,omitempty" yaml:"accessLogs,omitempty" export:"true"`
+	Tracing    *bool `json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" export:"true"`
+	Metrics    *bool `json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"`
+}
+
+// +k8s:deepcopy-gen=true
+
 // Mirroring holds the Mirroring configuration.
 type Mirroring struct {
 	Service     string          `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go
index 792ff48051..01c4640968 100644
--- a/pkg/config/dynamic/zz_generated.deepcopy.go
+++ b/pkg/config/dynamic/zz_generated.deepcopy.go
@@ -1023,6 +1023,7 @@ func (in *Model) DeepCopyInto(out *Model) {
 		*out = new(RouterTLSConfig)
 		(*in).DeepCopyInto(*out)
 	}
+	in.Observability.DeepCopyInto(&out.Observability)
 	return
 }
 
@@ -1249,6 +1250,11 @@ func (in *Router) DeepCopyInto(out *Router) {
 		*out = new(RouterTLSConfig)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Observability != nil {
+		in, out := &in.Observability, &out.Observability
+		*out = new(RouterObservabilityConfig)
+		(*in).DeepCopyInto(*out)
+	}
 	return
 }
 
@@ -1262,6 +1268,37 @@ func (in *Router) DeepCopy() *Router {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RouterObservabilityConfig) DeepCopyInto(out *RouterObservabilityConfig) {
+	*out = *in
+	if in.AccessLogs != nil {
+		in, out := &in.AccessLogs, &out.AccessLogs
+		*out = new(bool)
+		**out = **in
+	}
+	if in.Tracing != nil {
+		in, out := &in.Tracing, &out.Tracing
+		*out = new(bool)
+		**out = **in
+	}
+	if in.Metrics != nil {
+		in, out := &in.Metrics, &out.Metrics
+		*out = new(bool)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouterObservabilityConfig.
+func (in *RouterObservabilityConfig) DeepCopy() *RouterObservabilityConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(RouterObservabilityConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *RouterTCPTLSConfig) DeepCopyInto(out *RouterTCPTLSConfig) {
 	*out = *in
diff --git a/pkg/config/static/entrypoints.go b/pkg/config/static/entrypoints.go
index 6477da7e21..f443abe378 100644
--- a/pkg/config/static/entrypoints.go
+++ b/pkg/config/static/entrypoints.go
@@ -23,6 +23,7 @@ type EntryPoint struct {
 	HTTP2            *HTTP2Config          `description:"HTTP/2 configuration." json:"http2,omitempty" toml:"http2,omitempty" yaml:"http2,omitempty" export:"true"`
 	HTTP3            *HTTP3Config          `description:"HTTP/3 configuration." json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
 	UDP              *UDPConfig            `description:"UDP configuration." json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty"`
+	Observability    *ObservabilityConfig  `description:"Observability configuration." json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"`
 }
 
 // GetAddress strips any potential protocol part of the address field of the
@@ -59,6 +60,8 @@ func (ep *EntryPoint) SetDefaults() {
 	ep.HTTP.SetDefaults()
 	ep.HTTP2 = &HTTP2Config{}
 	ep.HTTP2.SetDefaults()
+	ep.Observability = &ObservabilityConfig{}
+	ep.Observability.SetDefaults()
 }
 
 // HTTPConfig is the HTTP configuration of an entry point.
@@ -158,3 +161,17 @@ type UDPConfig struct {
 func (u *UDPConfig) SetDefaults() {
 	u.Timeout = ptypes.Duration(DefaultUDPTimeout)
 }
+
+// ObservabilityConfig holds the observability configuration for an entry point.
+type ObservabilityConfig struct {
+	AccessLogs bool `json:"accessLogs,omitempty" toml:"accessLogs,omitempty" yaml:"accessLogs,omitempty" export:"true"`
+	Tracing    bool `json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" export:"true"`
+	Metrics    bool `json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"`
+}
+
+// SetDefaults sets the default values.
+func (o *ObservabilityConfig) SetDefaults() {
+	o.AccessLogs = true
+	o.Tracing = true
+	o.Metrics = true
+}
diff --git a/pkg/middlewares/metrics/metrics.go b/pkg/middlewares/metrics/metrics.go
index 1bbe4798dc..e8a1c6dbab 100644
--- a/pkg/middlewares/metrics/metrics.go
+++ b/pkg/middlewares/metrics/metrics.go
@@ -119,6 +119,11 @@ func (m *metricsMiddleware) GetTracingInformation() (string, string, trace.SpanK
 }
 
 func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+	if val := req.Context().Value(observability.DisableMetricsKey); val != nil {
+		m.next.ServeHTTP(rw, req)
+		return
+	}
+
 	proto := getRequestProtocol(req)
 
 	var labels []string
diff --git a/pkg/middlewares/observability/entrypoint.go b/pkg/middlewares/observability/entrypoint.go
index 5d1d1b8776..8b356b03b8 100644
--- a/pkg/middlewares/observability/entrypoint.go
+++ b/pkg/middlewares/observability/entrypoint.go
@@ -2,21 +2,15 @@ package observability
 
 import (
 	"context"
-	"fmt"
 	"net/http"
-	"strconv"
-	"strings"
 	"time"
 
 	"github.com/containous/alice"
 	"github.com/rs/zerolog/log"
-	"github.com/traefik/traefik/v3/pkg/metrics"
 	"github.com/traefik/traefik/v3/pkg/middlewares"
 	"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v3/pkg/tracing"
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
-	semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
 	"go.opentelemetry.io/otel/trace"
 	"go.opentelemetry.io/otel/trace/noop"
 )
@@ -28,24 +22,19 @@ const (
 type entryPointTracing struct {
 	tracer *tracing.Tracer
 
-	entryPoint            string
-	next                  http.Handler
-	semConvMetricRegistry *metrics.SemConvMetricsRegistry
+	entryPoint string
+	next       http.Handler
 }
 
-// WrapEntryPointHandler Wraps tracing to alice.Constructor.
-func WrapEntryPointHandler(ctx context.Context, tracer *tracing.Tracer, semConvMetricRegistry *metrics.SemConvMetricsRegistry, entryPointName string) alice.Constructor {
+// EntryPointHandler Wraps tracing to alice.Constructor.
+func EntryPointHandler(ctx context.Context, tracer *tracing.Tracer, entryPointName string) alice.Constructor {
 	return func(next http.Handler) (http.Handler, error) {
-		if tracer == nil {
-			tracer = tracing.NewTracer(noop.Tracer{}, nil, nil, nil)
-		}
-
-		return newEntryPoint(ctx, tracer, semConvMetricRegistry, entryPointName, next), nil
+		return newEntryPoint(ctx, tracer, entryPointName, next), nil
 	}
 }
 
 // newEntryPoint creates a new tracing middleware for incoming requests.
-func newEntryPoint(ctx context.Context, tracer *tracing.Tracer, semConvMetricRegistry *metrics.SemConvMetricsRegistry, entryPointName string, next http.Handler) http.Handler {
+func newEntryPoint(ctx context.Context, tracer *tracing.Tracer, entryPointName string, next http.Handler) http.Handler {
 	middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug().Msg("Creating middleware")
 
 	if tracer == nil {
@@ -53,10 +42,9 @@ func newEntryPoint(ctx context.Context, tracer *tracing.Tracer, semConvMetricReg
 	}
 
 	return &entryPointTracing{
-		entryPoint:            entryPointName,
-		tracer:                tracer,
-		semConvMetricRegistry: semConvMetricRegistry,
-		next:                  next,
+		entryPoint: entryPointName,
+		tracer:     tracer,
+		next:       next,
 	}
 }
 
@@ -88,23 +76,4 @@ func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request)
 
 	end := time.Now()
 	span.End(trace.WithTimestamp(end))
-
-	if e.semConvMetricRegistry != nil && e.semConvMetricRegistry.HTTPServerRequestDuration() != nil {
-		var attrs []attribute.KeyValue
-
-		if recorder.Status() < 100 || recorder.Status() >= 600 {
-			attrs = append(attrs, attribute.Key("error.type").String(fmt.Sprintf("Invalid HTTP status code ; %d", recorder.Status())))
-		} else if recorder.Status() >= 400 {
-			attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(recorder.Status())))
-		}
-
-		attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method))
-		attrs = append(attrs, semconv.HTTPResponseStatusCode(recorder.Status()))
-		attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto)))
-		attrs = append(attrs, semconv.NetworkProtocolVersion(Proto(req.Proto)))
-		attrs = append(attrs, semconv.ServerAddress(req.Host))
-		attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto")))
-
-		e.semConvMetricRegistry.HTTPServerRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), metric.WithAttributes(attrs...))
-	}
 }
diff --git a/pkg/middlewares/observability/observability.go b/pkg/middlewares/observability/observability.go
index 54f243186e..1ee9f3b99f 100644
--- a/pkg/middlewares/observability/observability.go
+++ b/pkg/middlewares/observability/observability.go
@@ -8,6 +8,11 @@ import (
 	"go.opentelemetry.io/otel/trace"
 )
 
+type contextKey int
+
+// DisableMetricsKey is a context key used to disable the metrics.
+const DisableMetricsKey contextKey = iota
+
 // SetStatusErrorf flags the span as in error and log an event.
 func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) {
 	if span := trace.SpanFromContext(ctx); span != nil {
diff --git a/pkg/middlewares/observability/semconv.go b/pkg/middlewares/observability/semconv.go
new file mode 100644
index 0000000000..51f4480b59
--- /dev/null
+++ b/pkg/middlewares/observability/semconv.go
@@ -0,0 +1,81 @@
+package observability
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/containous/alice"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v3/pkg/logs"
+	"github.com/traefik/traefik/v3/pkg/metrics"
+	"github.com/traefik/traefik/v3/pkg/middlewares"
+	"github.com/traefik/traefik/v3/pkg/middlewares/capture"
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric"
+	semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
+)
+
+const (
+	semConvServerMetricsTypeName = "SemConvServerMetrics"
+)
+
+type semConvServerMetrics struct {
+	next                  http.Handler
+	semConvMetricRegistry *metrics.SemConvMetricsRegistry
+}
+
+// SemConvServerMetricsHandler return the alice.Constructor for semantic conventions servers metrics.
+func SemConvServerMetricsHandler(ctx context.Context, semConvMetricRegistry *metrics.SemConvMetricsRegistry) alice.Constructor {
+	return func(next http.Handler) (http.Handler, error) {
+		return newServerMetricsSemConv(ctx, semConvMetricRegistry, next), nil
+	}
+}
+
+// newServerMetricsSemConv creates a new semConv server metrics middleware for incoming requests.
+func newServerMetricsSemConv(ctx context.Context, semConvMetricRegistry *metrics.SemConvMetricsRegistry, next http.Handler) http.Handler {
+	middlewares.GetLogger(ctx, "tracing", semConvServerMetricsTypeName).Debug().Msg("Creating middleware")
+
+	return &semConvServerMetrics{
+		semConvMetricRegistry: semConvMetricRegistry,
+		next:                  next,
+	}
+}
+
+func (e *semConvServerMetrics) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+	if e.semConvMetricRegistry == nil || e.semConvMetricRegistry.HTTPServerRequestDuration() == nil {
+		e.next.ServeHTTP(rw, req)
+		return
+	}
+
+	start := time.Now()
+	e.next.ServeHTTP(rw, req)
+	end := time.Now()
+
+	ctx := req.Context()
+	capt, err := capture.FromContext(ctx)
+	if err != nil {
+		log.Ctx(ctx).Error().Err(err).Str(logs.MiddlewareType, semConvServerMetricsTypeName).Msg("Could not get Capture")
+		return
+	}
+
+	var attrs []attribute.KeyValue
+
+	if capt.StatusCode() < 100 || capt.StatusCode() >= 600 {
+		attrs = append(attrs, attribute.Key("error.type").String(fmt.Sprintf("Invalid HTTP status code ; %d", capt.StatusCode())))
+	} else if capt.StatusCode() >= 400 {
+		attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(capt.StatusCode())))
+	}
+
+	attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method))
+	attrs = append(attrs, semconv.HTTPResponseStatusCode(capt.StatusCode()))
+	attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto)))
+	attrs = append(attrs, semconv.NetworkProtocolVersion(Proto(req.Proto)))
+	attrs = append(attrs, semconv.ServerAddress(req.Host))
+	attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto")))
+
+	e.semConvMetricRegistry.HTTPServerRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), metric.WithAttributes(attrs...))
+}
diff --git a/pkg/provider/kubernetes/crd/fixtures/simple.yml b/pkg/provider/kubernetes/crd/fixtures/simple.yml
index 36d4313382..3465c439ae 100644
--- a/pkg/provider/kubernetes/crd/fixtures/simple.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/simple.yml
@@ -12,6 +12,10 @@ spec:
   - match: Host(`foo.com`) && PathPrefix(`/bar`)
     kind: Rule
     priority: 12
+    observability:
+      accessLogs: true
+      tracing: true
+      metrics: true
     services:
     - name: whoami
       port: 80
diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go
index fb972a6c0b..5c548a8ed3 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_http.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_http.go
@@ -114,12 +114,13 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 			}
 
 			r := &dynamic.Router{
-				Middlewares: mds,
-				Priority:    route.Priority,
-				RuleSyntax:  route.Syntax,
-				EntryPoints: ingressRoute.Spec.EntryPoints,
-				Rule:        route.Match,
-				Service:     serviceName,
+				Middlewares:   mds,
+				Priority:      route.Priority,
+				RuleSyntax:    route.Syntax,
+				EntryPoints:   ingressRoute.Spec.EntryPoints,
+				Rule:          route.Match,
+				Service:       serviceName,
+				Observability: route.Observability,
 			}
 
 			if ingressRoute.Spec.TLS != nil {
diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go
index 7e686562ea..3a46caf9f6 100644
--- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go
+++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go
@@ -43,6 +43,9 @@ type Route struct {
 	// Middlewares defines the list of references to Middleware resources.
 	// More info: https://doc.traefik.io/traefik/v3.2/routing/providers/kubernetes-crd/#kind-middleware
 	Middlewares []MiddlewareRef `json:"middlewares,omitempty"`
+	// Observability defines the observability configuration for a router.
+	// More info: https://doc.traefik.io/traefik/v3.2/routing/routers/#observability
+	Observability *dynamic.RouterObservabilityConfig `json:"observability,omitempty"`
 }
 
 // TLS holds the TLS configuration.
diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
index 466cc75779..45287c4c98 100644
--- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
@@ -1102,6 +1102,11 @@ func (in *Route) DeepCopyInto(out *Route) {
 		*out = make([]MiddlewareRef, len(*in))
 		copy(*out, *in)
 	}
+	if in.Observability != nil {
+		in, out := &in.Observability, &out.Observability
+		*out = new(dynamic.RouterObservabilityConfig)
+		(*in).DeepCopyInto(*out)
+	}
 	return
 }
 
diff --git a/pkg/provider/kubernetes/ingress/annotations.go b/pkg/provider/kubernetes/ingress/annotations.go
index 144dd2a460..fe7f52d52b 100644
--- a/pkg/provider/kubernetes/ingress/annotations.go
+++ b/pkg/provider/kubernetes/ingress/annotations.go
@@ -22,12 +22,13 @@ type RouterConfig struct {
 
 // RouterIng is the router's configuration from annotations.
 type RouterIng struct {
-	PathMatcher string                   `json:"pathMatcher,omitempty"`
-	EntryPoints []string                 `json:"entryPoints,omitempty"`
-	Middlewares []string                 `json:"middlewares,omitempty"`
-	Priority    int                      `json:"priority,omitempty"`
-	RuleSyntax  string                   `json:"ruleSyntax,omitempty"`
-	TLS         *dynamic.RouterTLSConfig `json:"tls,omitempty" label:"allowEmpty"`
+	PathMatcher   string                             `json:"pathMatcher,omitempty"`
+	EntryPoints   []string                           `json:"entryPoints,omitempty"`
+	Middlewares   []string                           `json:"middlewares,omitempty"`
+	Priority      int                                `json:"priority,omitempty"`
+	RuleSyntax    string                             `json:"ruleSyntax,omitempty"`
+	TLS           *dynamic.RouterTLSConfig           `json:"tls,omitempty" label:"allowEmpty"`
+	Observability *dynamic.RouterObservabilityConfig `json:"observability,omitempty" label:"allowEmpty"`
 }
 
 // SetDefaults sets the default values.
diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-annotations.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-annotations.yml
index 3a57a63458..e910efefae 100644
--- a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-annotations.yml
+++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-annotations.yml
@@ -18,6 +18,9 @@ metadata:
     traefik.ingress.kubernetes.io/router.tls.domains.1.main: example.com
     traefik.ingress.kubernetes.io/router.tls.domains.1.sans: one.example.com,two.example.com
     traefik.ingress.kubernetes.io/router.tls.options: foobar
+    traefik.ingress.kubernetes.io/router.observability.accesslogs: "true"
+    traefik.ingress.kubernetes.io/router.observability.metrics: "true"
+    traefik.ingress.kubernetes.io/router.observability.tracing: "true"
 
 spec:
   rules:
diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go
index ad53bb5eb2..e1fce4e1f8 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes.go
@@ -293,6 +293,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 				rt.EntryPoints = rtConfig.Router.EntryPoints
 				rt.Middlewares = rtConfig.Router.Middlewares
 				rt.TLS = rtConfig.Router.TLS
+				rt.Observability = rtConfig.Router.Observability
 			}
 
 			p.applyRouterTransform(ctxIngress, rt, ingress)
@@ -619,10 +620,8 @@ func (p *Provider) loadRouter(rule netv1.IngressRule, pa netv1.HTTPIngressPath,
 		rt.Priority = rtConfig.Router.Priority
 		rt.EntryPoints = rtConfig.Router.EntryPoints
 		rt.Middlewares = rtConfig.Router.Middlewares
-
-		if rtConfig.Router.TLS != nil {
-			rt.TLS = rtConfig.Router.TLS
-		}
+		rt.TLS = rtConfig.Router.TLS
+		rt.Observability = rtConfig.Router.Observability
 	}
 
 	var rules []string
diff --git a/pkg/provider/traefik/fixtures/models.json b/pkg/provider/traefik/fixtures/models.json
index 005b6bf9a5..65a6c88b0b 100644
--- a/pkg/provider/traefik/fixtures/models.json
+++ b/pkg/provider/traefik/fixtures/models.json
@@ -27,6 +27,11 @@
               ]
             }
           ]
+        },
+        "observability": {
+          "accessLogs": false,
+          "tracing": false,
+          "metrics": false
         }
       }
     }
diff --git a/pkg/provider/traefik/internal.go b/pkg/provider/traefik/internal.go
index 917c3d44d4..e65ab64dc2 100644
--- a/pkg/provider/traefik/internal.go
+++ b/pkg/provider/traefik/internal.go
@@ -231,6 +231,14 @@ func (i *Provider) entryPointModels(cfg *dynamic.Configuration) {
 			Middlewares: ep.HTTP.Middlewares,
 		}
 
+		if ep.Observability != nil {
+			m.Observability = dynamic.RouterObservabilityConfig{
+				AccessLogs: &ep.Observability.AccessLogs,
+				Tracing:    &ep.Observability.Tracing,
+				Metrics:    &ep.Observability.Metrics,
+			}
+		}
+
 		if ep.HTTP.TLS != nil {
 			m.TLS = &dynamic.RouterTLSConfig{
 				Options:      ep.HTTP.TLS.Options,
diff --git a/pkg/proxy/httputil/observability.go b/pkg/proxy/httputil/observability.go
index 9240f5f7ec..8fa3382e39 100644
--- a/pkg/proxy/httputil/observability.go
+++ b/pkg/proxy/httputil/observability.go
@@ -68,7 +68,7 @@ func (t *wrapper) RoundTrip(req *http.Request) (*http.Response, error) {
 		span.End(trace.WithTimestamp(end))
 	}
 
-	if t.semConvMetricRegistry != nil && t.semConvMetricRegistry.HTTPClientRequestDuration() != nil {
+	if req.Context().Value(observability.DisableMetricsKey) == nil && t.semConvMetricRegistry != nil && t.semConvMetricRegistry.HTTPClientRequestDuration() != nil {
 		var attrs []attribute.KeyValue
 
 		if statusCode < 100 || statusCode >= 600 {
diff --git a/pkg/server/aggregator.go b/pkg/server/aggregator.go
index f015cb43d1..0a849e3d8c 100644
--- a/pkg/server/aggregator.go
+++ b/pkg/server/aggregator.go
@@ -178,6 +178,22 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration {
 
 					cp.Middlewares = append(m.Middlewares, cp.Middlewares...)
 
+					if cp.Observability == nil {
+						cp.Observability = &dynamic.RouterObservabilityConfig{}
+					}
+
+					if cp.Observability.AccessLogs == nil {
+						cp.Observability.AccessLogs = m.Observability.AccessLogs
+					}
+
+					if cp.Observability.Tracing == nil {
+						cp.Observability.Tracing = m.Observability.Tracing
+					}
+
+					if cp.Observability.Metrics == nil {
+						cp.Observability.Metrics = m.Observability.Metrics
+					}
+
 					rtName := name
 					if len(eps) > 1 {
 						rtName = epName + "-" + name
diff --git a/pkg/server/middleware/observability.go b/pkg/server/middleware/observability.go
index 902938acb4..566d8522ca 100644
--- a/pkg/server/middleware/observability.go
+++ b/pkg/server/middleware/observability.go
@@ -8,12 +8,13 @@ import (
 
 	"github.com/containous/alice"
 	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v3/pkg/config/dynamic"
 	"github.com/traefik/traefik/v3/pkg/config/static"
 	"github.com/traefik/traefik/v3/pkg/logs"
 	"github.com/traefik/traefik/v3/pkg/metrics"
 	"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v3/pkg/middlewares/capture"
-	metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
+	mmetrics "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
 	"github.com/traefik/traefik/v3/pkg/middlewares/observability"
 	"github.com/traefik/traefik/v3/pkg/tracing"
 )
@@ -41,7 +42,7 @@ func NewObservabilityMgr(config static.Configuration, metricsRegistry metrics.Re
 }
 
 // BuildEPChain an observability middleware chain by entry point.
-func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName string, resourceName string) alice.Chain {
+func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName string, resourceName string, observabilityConfig *dynamic.RouterObservabilityConfig) alice.Chain {
 	chain := alice.New()
 
 	if o == nil {
@@ -49,62 +50,101 @@ func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName stri
 	}
 
 	if o.accessLoggerMiddleware != nil || o.metricsRegistry != nil && (o.metricsRegistry.IsEpEnabled() || o.metricsRegistry.IsRouterEnabled() || o.metricsRegistry.IsSvcEnabled()) {
-		if o.ShouldAddAccessLogs(resourceName) || o.ShouldAddMetrics(resourceName) {
+		if o.ShouldAddAccessLogs(resourceName, observabilityConfig) || o.ShouldAddMetrics(resourceName, observabilityConfig) {
 			chain = chain.Append(capture.Wrap)
 		}
 	}
 
 	// As the Entry point observability middleware ensures that the tracing is added to the request and logger context,
 	// it needs to be added before the access log middleware to ensure that the trace ID is logged.
-	if (o.tracer != nil && o.ShouldAddTracing(resourceName)) || (o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName)) {
-		chain = chain.Append(observability.WrapEntryPointHandler(ctx, o.tracer, o.semConvMetricRegistry, entryPointName))
+	if o.tracer != nil && o.ShouldAddTracing(resourceName, observabilityConfig) {
+		chain = chain.Append(observability.EntryPointHandler(ctx, o.tracer, entryPointName))
 	}
 
-	if o.accessLoggerMiddleware != nil && o.ShouldAddAccessLogs(resourceName) {
+	if o.accessLoggerMiddleware != nil && o.ShouldAddAccessLogs(resourceName, observabilityConfig) {
 		chain = chain.Append(accesslog.WrapHandler(o.accessLoggerMiddleware))
 		chain = chain.Append(func(next http.Handler) (http.Handler, error) {
 			return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.InitServiceFields), nil
 		})
 	}
 
-	if o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName) {
-		metricsHandler := metricsMiddle.WrapEntryPointHandler(ctx, o.metricsRegistry, entryPointName)
+	// Semantic convention server metrics handler.
+	if o.semConvMetricRegistry != nil && o.ShouldAddMetrics(resourceName, observabilityConfig) {
+		chain = chain.Append(observability.SemConvServerMetricsHandler(ctx, o.semConvMetricRegistry))
+	}
+
+	if o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName, observabilityConfig) {
+		metricsHandler := mmetrics.WrapEntryPointHandler(ctx, o.metricsRegistry, entryPointName)
 
-		if o.tracer != nil && o.ShouldAddTracing(resourceName) {
+		if o.tracer != nil && o.ShouldAddTracing(resourceName, observabilityConfig) {
 			chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler))
 		} else {
 			chain = chain.Append(metricsHandler)
 		}
 	}
 
+	// Inject context keys to control whether to produce metrics further downstream (services, round-tripper),
+	// because the router configuration cannot be evaluated during build time for services.
+	if observabilityConfig != nil && observabilityConfig.Metrics != nil && !*observabilityConfig.Metrics {
+		chain = chain.Append(func(next http.Handler) (http.Handler, error) {
+			return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+				next.ServeHTTP(rw, req.WithContext(context.WithValue(req.Context(), observability.DisableMetricsKey, true)))
+			}), nil
+		})
+	}
+
 	return chain
 }
 
-// ShouldAddAccessLogs returns whether the access logs should be enabled for the given resource.
-func (o *ObservabilityMgr) ShouldAddAccessLogs(resourceName string) bool {
+// ShouldAddAccessLogs returns whether the access logs should be enabled for the given serviceName and the observability config.
+func (o *ObservabilityMgr) ShouldAddAccessLogs(serviceName string, observabilityConfig *dynamic.RouterObservabilityConfig) bool {
 	if o == nil {
 		return false
 	}
 
-	return o.config.AccessLog != nil && (o.config.AccessLog.AddInternals || !strings.HasSuffix(resourceName, "@internal"))
+	if o.config.AccessLog == nil {
+		return false
+	}
+
+	if strings.HasSuffix(serviceName, "@internal") && !o.config.AccessLog.AddInternals {
+		return false
+	}
+
+	return observabilityConfig == nil || observabilityConfig.AccessLogs != nil && *observabilityConfig.AccessLogs
 }
 
-// ShouldAddMetrics returns whether the metrics should be enabled for the given resource.
-func (o *ObservabilityMgr) ShouldAddMetrics(resourceName string) bool {
+// ShouldAddMetrics returns whether the metrics should be enabled for the given resource and the observability config.
+func (o *ObservabilityMgr) ShouldAddMetrics(serviceName string, observabilityConfig *dynamic.RouterObservabilityConfig) bool {
 	if o == nil {
 		return false
 	}
 
-	return o.config.Metrics != nil && (o.config.Metrics.AddInternals || !strings.HasSuffix(resourceName, "@internal"))
+	if o.config.Metrics == nil {
+		return false
+	}
+
+	if strings.HasSuffix(serviceName, "@internal") && !o.config.Metrics.AddInternals {
+		return false
+	}
+
+	return observabilityConfig == nil || observabilityConfig.Metrics != nil && *observabilityConfig.Metrics
 }
 
-// ShouldAddTracing returns whether the tracing should be enabled for the given resource.
-func (o *ObservabilityMgr) ShouldAddTracing(resourceName string) bool {
+// ShouldAddTracing returns whether the tracing should be enabled for the given serviceName and the observability config.
+func (o *ObservabilityMgr) ShouldAddTracing(serviceName string, observabilityConfig *dynamic.RouterObservabilityConfig) bool {
 	if o == nil {
 		return false
 	}
 
-	return o.config.Tracing != nil && (o.config.Tracing.AddInternals || !strings.HasSuffix(resourceName, "@internal"))
+	if o.config.Tracing == nil {
+		return false
+	}
+
+	if strings.HasSuffix(serviceName, "@internal") && !o.config.Tracing.AddInternals {
+		return false
+	}
+
+	return observabilityConfig == nil || observabilityConfig.Tracing != nil && *observabilityConfig.Tracing
 }
 
 // MetricsRegistry is an accessor to the metrics registry.
diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go
index e3af98f33c..fcab90a942 100644
--- a/pkg/server/router/router.go
+++ b/pkg/server/router/router.go
@@ -91,12 +91,12 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
 			continue
 		}
 
-		handler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "").Then(BuildDefaultHTTPRouter())
+		defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "", nil).Then(BuildDefaultHTTPRouter())
 		if err != nil {
 			logger.Error().Err(err).Send()
 			continue
 		}
-		entryPointHandlers[entryPointName] = handler
+		entryPointHandlers[entryPointName] = defaultHandler
 	}
 
 	return entryPointHandlers
@@ -108,7 +108,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName str
 		return nil, err
 	}
 
-	defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "defaultHandler").Then(http.NotFoundHandler())
+	defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "", nil).Then(http.NotFoundHandler())
 	if err != nil {
 		return nil, err
 	}
@@ -137,7 +137,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName str
 			continue
 		}
 
-		observabilityChain := m.observabilityMgr.BuildEPChain(ctx, entryPointName, routerConfig.Service)
+		observabilityChain := m.observabilityMgr.BuildEPChain(ctx, entryPointName, routerConfig.Service, routerConfig.Observability)
 		handler, err = observabilityChain.Then(handler)
 		if err != nil {
 			routerConfig.AddError(err, true)
@@ -182,7 +182,7 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou
 	}
 
 	// Prevents from enabling observability for internal resources.
-	if !m.observabilityMgr.ShouldAddAccessLogs(provider.GetQualifiedName(ctx, routerConfig.Service)) {
+	if !m.observabilityMgr.ShouldAddAccessLogs(provider.GetQualifiedName(ctx, routerConfig.Service), routerConfig.Observability) {
 		m.routerHandlers[routerName] = handler
 		return m.routerHandlers[routerName], nil
 	}
@@ -221,12 +221,12 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterIn
 	chain := alice.New()
 
 	if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsRouterEnabled() &&
-		m.observabilityMgr.ShouldAddMetrics(provider.GetQualifiedName(ctx, router.Service)) {
+		m.observabilityMgr.ShouldAddMetrics(provider.GetQualifiedName(ctx, router.Service), router.Observability) {
 		chain = chain.Append(metricsMiddle.WrapRouterHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, provider.GetQualifiedName(ctx, router.Service)))
 	}
 
 	// Prevents from enabling tracing for internal resources.
-	if !m.observabilityMgr.ShouldAddTracing(provider.GetQualifiedName(ctx, router.Service)) {
+	if !m.observabilityMgr.ShouldAddTracing(provider.GetQualifiedName(ctx, router.Service), router.Observability) {
 		return chain.Extend(*mHandler).Then(sHandler)
 	}
 
diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go
index 602c7bd456..a4c5135e49 100644
--- a/pkg/server/service/service.go
+++ b/pkg/server/service/service.go
@@ -356,7 +356,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
 
 		qualifiedSvcName := provider.GetQualifiedName(ctx, serviceName)
 
-		shouldObserve := m.observabilityMgr.ShouldAddTracing(qualifiedSvcName) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName)
+		shouldObserve := m.observabilityMgr.ShouldAddTracing(qualifiedSvcName, nil) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName, nil)
 		proxy, err := m.proxyBuilder.Build(service.ServersTransport, target, shouldObserve, passHostHeader, server.PreservePath, flushInterval)
 		if err != nil {
 			return nil, fmt.Errorf("error building proxy for server URL %s: %w", server.URL, err)
@@ -364,14 +364,14 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
 
 		// Prevents from enabling observability for internal resources.
 
-		if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName) {
+		if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName, nil) {
 			proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceURL, target.String(), nil)
 			proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceAddr, target.Host, nil)
 			proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, serviceName, accesslog.AddServiceFields)
 		}
 
 		if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsSvcEnabled() &&
-			m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName) {
+			m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName, nil) {
 			metricsHandler := metricsMiddle.WrapServiceHandler(ctx, m.observabilityMgr.MetricsRegistry(), serviceName)
 
 			proxy, err = alice.New().
@@ -382,11 +382,11 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
 			}
 		}
 
-		if m.observabilityMgr.ShouldAddTracing(qualifiedSvcName) {
+		if m.observabilityMgr.ShouldAddTracing(qualifiedSvcName, nil) {
 			proxy = observability.NewService(ctx, serviceName, proxy)
 		}
 
-		if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName) {
+		if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName, nil) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName, nil) {
 			// Some piece of middleware, like the ErrorPage, are relying on this serviceBuilder to get the handler for a given service,
 			// to re-target the request to it.
 			// Those pieces of middleware can be configured on routes that expose a Traefik internal service.


Test patch:
diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go
index 7f5e9909b4..553689adc0 100644
--- a/pkg/config/label/label_test.go
+++ b/pkg/config/label/label_test.go
@@ -880,6 +880,11 @@ func TestEncodeConfiguration(t *testing.T) {
 					Rule:     "foobar",
 					Priority: 42,
 					TLS:      &dynamic.RouterTLSConfig{},
+					Observability: &dynamic.RouterObservabilityConfig{
+						AccessLogs: pointer(true),
+						Tracing:    pointer(true),
+						Metrics:    pointer(true),
+					},
 				},
 				"Router1": {
 					EntryPoints: []string{
@@ -893,6 +898,11 @@ func TestEncodeConfiguration(t *testing.T) {
 					Service:  "foobar",
 					Rule:     "foobar",
 					Priority: 42,
+					Observability: &dynamic.RouterObservabilityConfig{
+						AccessLogs: pointer(true),
+						Tracing:    pointer(true),
+						Metrics:    pointer(true),
+					},
 				},
 			},
 			Middlewares: map[string]*dynamic.Middleware{
@@ -1405,17 +1415,23 @@ func TestEncodeConfiguration(t *testing.T) {
 		"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.aaa":                                  "foo1",
 		"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.bbb":                                  "foo2",
 
-		"traefik.HTTP.Routers.Router0.EntryPoints": "foobar, fiibar",
-		"traefik.HTTP.Routers.Router0.Middlewares": "foobar, fiibar",
-		"traefik.HTTP.Routers.Router0.Priority":    "42",
-		"traefik.HTTP.Routers.Router0.Rule":        "foobar",
-		"traefik.HTTP.Routers.Router0.Service":     "foobar",
-		"traefik.HTTP.Routers.Router0.TLS":         "true",
-		"traefik.HTTP.Routers.Router1.EntryPoints": "foobar, fiibar",
-		"traefik.HTTP.Routers.Router1.Middlewares": "foobar, fiibar",
-		"traefik.HTTP.Routers.Router1.Priority":    "42",
-		"traefik.HTTP.Routers.Router1.Rule":        "foobar",
-		"traefik.HTTP.Routers.Router1.Service":     "foobar",
+		"traefik.HTTP.Routers.Router0.EntryPoints":              "foobar, fiibar",
+		"traefik.HTTP.Routers.Router0.Middlewares":              "foobar, fiibar",
+		"traefik.HTTP.Routers.Router0.Priority":                 "42",
+		"traefik.HTTP.Routers.Router0.Rule":                     "foobar",
+		"traefik.HTTP.Routers.Router0.Service":                  "foobar",
+		"traefik.HTTP.Routers.Router0.TLS":                      "true",
+		"traefik.HTTP.Routers.Router0.Observability.AccessLogs": "true",
+		"traefik.HTTP.Routers.Router0.Observability.Tracing":    "true",
+		"traefik.HTTP.Routers.Router0.Observability.Metrics":    "true",
+		"traefik.HTTP.Routers.Router1.EntryPoints":              "foobar, fiibar",
+		"traefik.HTTP.Routers.Router1.Middlewares":              "foobar, fiibar",
+		"traefik.HTTP.Routers.Router1.Priority":                 "42",
+		"traefik.HTTP.Routers.Router1.Rule":                     "foobar",
+		"traefik.HTTP.Routers.Router1.Service":                  "foobar",
+		"traefik.HTTP.Routers.Router1.Observability.AccessLogs": "true",
+		"traefik.HTTP.Routers.Router1.Observability.Tracing":    "true",
+		"traefik.HTTP.Routers.Router1.Observability.Metrics":    "true",
 
 		"traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Headers.name0":        "foobar",
 		"traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Headers.name1":        "foobar",
diff --git a/pkg/config/static/static_config_test.go b/pkg/config/static/static_config_test.go
index 67c643a32a..78633bc2f1 100644
--- a/pkg/config/static/static_config_test.go
+++ b/pkg/config/static/static_config_test.go
@@ -77,6 +77,11 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) {
 					UDP: &UDPConfig{
 						Timeout: 3000000000,
 					},
+					Observability: &ObservabilityConfig{
+						AccessLogs: true,
+						Tracing:    true,
+						Metrics:    true,
+					},
 				}},
 				Providers: &Providers{},
 			},
@@ -122,6 +127,11 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) {
 					UDP: &UDPConfig{
 						Timeout: 3000000000,
 					},
+					Observability: &ObservabilityConfig{
+						AccessLogs: true,
+						Tracing:    true,
+						Metrics:    true,
+					},
 				}},
 				Providers: &Providers{},
 				CertificatesResolvers: map[string]CertificateResolver{
@@ -178,6 +188,11 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) {
 					UDP: &UDPConfig{
 						Timeout: 3000000000,
 					},
+					Observability: &ObservabilityConfig{
+						AccessLogs: true,
+						Tracing:    true,
+						Metrics:    true,
+					},
 				}},
 				Providers: &Providers{},
 				CertificatesResolvers: map[string]CertificateResolver{
@@ -238,6 +253,11 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) {
 					UDP: &UDPConfig{
 						Timeout: 3000000000,
 					},
+					Observability: &ObservabilityConfig{
+						AccessLogs: true,
+						Tracing:    true,
+						Metrics:    true,
+					},
 				}},
 				Providers: &Providers{},
 				CertificatesResolvers: map[string]CertificateResolver{
diff --git a/pkg/middlewares/observability/entrypoint_test.go b/pkg/middlewares/observability/entrypoint_test.go
index 3e7a90870d..0b94f5fd52 100644
--- a/pkg/middlewares/observability/entrypoint_test.go
+++ b/pkg/middlewares/observability/entrypoint_test.go
@@ -5,19 +5,11 @@ import (
 	"net/http"
 	"net/http/httptest"
 	"testing"
-	"time"
 
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-	ptypes "github.com/traefik/paerser/types"
-	"github.com/traefik/traefik/v3/pkg/metrics"
 	"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v3/pkg/tracing"
-	"github.com/traefik/traefik/v3/pkg/types"
 	"go.opentelemetry.io/otel/attribute"
-	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
-	"go.opentelemetry.io/otel/sdk/metric/metricdata"
-	"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
 )
 
 func TestEntryPointMiddleware_tracing(t *testing.T) {
@@ -77,7 +69,7 @@ func TestEntryPointMiddleware_tracing(t *testing.T) {
 
 			tracer := &mockTracer{}
 
-			handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}, []string{"q"}), nil, test.entryPoint, next)
+			handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}, []string{"q"}), test.entryPoint, next)
 			handler.ServeHTTP(rw, req)
 
 			for _, span := range tracer.spans {
@@ -88,101 +80,6 @@ func TestEntryPointMiddleware_tracing(t *testing.T) {
 	}
 }
 
-func TestEntryPointMiddleware_metrics(t *testing.T) {
-	tests := []struct {
-		desc           string
-		statusCode     int
-		wantAttributes attribute.Set
-	}{
-		{
-			desc:       "not found status",
-			statusCode: http.StatusNotFound,
-			wantAttributes: attribute.NewSet(
-				attribute.Key("error.type").String("404"),
-				attribute.Key("http.request.method").String("GET"),
-				attribute.Key("http.response.status_code").Int(404),
-				attribute.Key("network.protocol.name").String("http/1.1"),
-				attribute.Key("network.protocol.version").String("1.1"),
-				attribute.Key("server.address").String("www.test.com"),
-				attribute.Key("url.scheme").String("http"),
-			),
-		},
-		{
-			desc:       "created status",
-			statusCode: http.StatusCreated,
-			wantAttributes: attribute.NewSet(
-				attribute.Key("http.request.method").String("GET"),
-				attribute.Key("http.response.status_code").Int(201),
-				attribute.Key("network.protocol.name").String("http/1.1"),
-				attribute.Key("network.protocol.version").String("1.1"),
-				attribute.Key("server.address").String("www.test.com"),
-				attribute.Key("url.scheme").String("http"),
-			),
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.desc, func(t *testing.T) {
-			t.Parallel()
-
-			var cfg types.OTLP
-			(&cfg).SetDefaults()
-			cfg.AddRoutersLabels = true
-			cfg.PushInterval = ptypes.Duration(10 * time.Millisecond)
-			rdr := sdkmetric.NewManualReader()
-
-			meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(rdr))
-			// force the meter provider with manual reader to collect metrics for the test.
-			metrics.SetMeterProvider(meterProvider)
-
-			semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(context.Background(), &cfg)
-			require.NoError(t, err)
-			require.NotNil(t, semConvMetricRegistry)
-
-			req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil)
-			rw := httptest.NewRecorder()
-			req.RemoteAddr = "10.0.0.1:1234"
-			req.Header.Set("User-Agent", "entrypoint-test")
-			req.Header.Set("X-Forwarded-Proto", "http")
-
-			next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
-				rw.WriteHeader(test.statusCode)
-			})
-
-			handler := newEntryPoint(context.Background(), nil, semConvMetricRegistry, "test", next)
-			handler.ServeHTTP(rw, req)
-
-			got := metricdata.ResourceMetrics{}
-			err = rdr.Collect(context.Background(), &got)
-			require.NoError(t, err)
-
-			require.Len(t, got.ScopeMetrics, 1)
-
-			expected := metricdata.Metrics{
-				Name:        "http.server.request.duration",
-				Description: "Duration of HTTP server requests.",
-				Unit:        "s",
-				Data: metricdata.Histogram[float64]{
-					DataPoints: []metricdata.HistogramDataPoint[float64]{
-						{
-							Attributes:   test.wantAttributes,
-							Count:        1,
-							Bounds:       []float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10},
-							BucketCounts: []uint64{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
-							Min:          metricdata.NewExtrema[float64](1),
-							Max:          metricdata.NewExtrema[float64](1),
-							Sum:          1,
-						},
-					},
-					Temporality: metricdata.CumulativeTemporality,
-				},
-			}
-
-			metricdatatest.AssertEqual[metricdata.Metrics](t, expected, got.ScopeMetrics[0].Metrics[0], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue())
-		})
-	}
-}
-
 func TestEntryPointMiddleware_tracingInfoIntoLog(t *testing.T) {
 	req := httptest.NewRequest(http.MethodGet, "http://www.test.com/", http.NoBody)
 	req = req.WithContext(
@@ -197,7 +94,7 @@ func TestEntryPointMiddleware_tracingInfoIntoLog(t *testing.T) {
 
 	tracer := &mockTracer{}
 
-	handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{}, []string{}, []string{}), nil, "test", next)
+	handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{}, []string{}, []string{}), "test", next)
 	handler.ServeHTTP(httptest.NewRecorder(), req)
 
 	expectedSpanCtx := tracer.spans[0].SpanContext()
diff --git a/pkg/middlewares/observability/semconv_test.go b/pkg/middlewares/observability/semconv_test.go
new file mode 100644
index 0000000000..08846c4d7c
--- /dev/null
+++ b/pkg/middlewares/observability/semconv_test.go
@@ -0,0 +1,118 @@
+package observability
+
+import (
+	"context"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+	ptypes "github.com/traefik/paerser/types"
+	"github.com/traefik/traefik/v3/pkg/metrics"
+	"github.com/traefik/traefik/v3/pkg/middlewares/capture"
+	"github.com/traefik/traefik/v3/pkg/types"
+	"go.opentelemetry.io/otel/attribute"
+	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
+	"go.opentelemetry.io/otel/sdk/metric/metricdata"
+	"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
+)
+
+func TestSemConvServerMetrics(t *testing.T) {
+	tests := []struct {
+		desc           string
+		statusCode     int
+		wantAttributes attribute.Set
+	}{
+		{
+			desc:       "not found status",
+			statusCode: http.StatusNotFound,
+			wantAttributes: attribute.NewSet(
+				attribute.Key("error.type").String("404"),
+				attribute.Key("http.request.method").String("GET"),
+				attribute.Key("http.response.status_code").Int(404),
+				attribute.Key("network.protocol.name").String("http/1.1"),
+				attribute.Key("network.protocol.version").String("1.1"),
+				attribute.Key("server.address").String("www.test.com"),
+				attribute.Key("url.scheme").String("http"),
+			),
+		},
+		{
+			desc:       "created status",
+			statusCode: http.StatusCreated,
+			wantAttributes: attribute.NewSet(
+				attribute.Key("http.request.method").String("GET"),
+				attribute.Key("http.response.status_code").Int(201),
+				attribute.Key("network.protocol.name").String("http/1.1"),
+				attribute.Key("network.protocol.version").String("1.1"),
+				attribute.Key("server.address").String("www.test.com"),
+				attribute.Key("url.scheme").String("http"),
+			),
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			t.Parallel()
+
+			var cfg types.OTLP
+			(&cfg).SetDefaults()
+			cfg.AddRoutersLabels = true
+			cfg.PushInterval = ptypes.Duration(10 * time.Millisecond)
+			rdr := sdkmetric.NewManualReader()
+
+			meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(rdr))
+			// force the meter provider with manual reader to collect metrics for the test.
+			metrics.SetMeterProvider(meterProvider)
+
+			semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(context.Background(), &cfg)
+			require.NoError(t, err)
+			require.NotNil(t, semConvMetricRegistry)
+
+			req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil)
+			rw := httptest.NewRecorder()
+			req.RemoteAddr = "10.0.0.1:1234"
+			req.Header.Set("User-Agent", "entrypoint-test")
+			req.Header.Set("X-Forwarded-Proto", "http")
+
+			next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
+				rw.WriteHeader(test.statusCode)
+			})
+
+			handler := newServerMetricsSemConv(context.Background(), semConvMetricRegistry, next)
+
+			handler, err = capture.Wrap(handler)
+			require.NoError(t, err)
+
+			handler.ServeHTTP(rw, req)
+
+			got := metricdata.ResourceMetrics{}
+			err = rdr.Collect(context.Background(), &got)
+			require.NoError(t, err)
+
+			require.Len(t, got.ScopeMetrics, 1)
+
+			expected := metricdata.Metrics{
+				Name:        "http.server.request.duration",
+				Description: "Duration of HTTP server requests.",
+				Unit:        "s",
+				Data: metricdata.Histogram[float64]{
+					DataPoints: []metricdata.HistogramDataPoint[float64]{
+						{
+							Attributes:   test.wantAttributes,
+							Count:        1,
+							Bounds:       []float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10},
+							BucketCounts: []uint64{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+							Min:          metricdata.NewExtrema[float64](1),
+							Max:          metricdata.NewExtrema[float64](1),
+							Sum:          1,
+						},
+					},
+					Temporality: metricdata.CumulativeTemporality,
+				},
+			}
+
+			metricdatatest.AssertEqual[metricdata.Metrics](t, expected, got.ScopeMetrics[0].Metrics[0], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue())
+		})
+	}
+}
diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go
index 29856eef11..a7ceb391e8 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_test.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_test.go
@@ -1688,6 +1688,11 @@ func TestLoadIngressRoutes(t *testing.T) {
 							Service:     "default-test-route-6b204d94623b3df4370c",
 							Rule:        "Host(`foo.com`) && PathPrefix(`/bar`)",
 							Priority:    12,
+							Observability: &dynamic.RouterObservabilityConfig{
+								AccessLogs: pointer(true),
+								Tracing:    pointer(true),
+								Metrics:    pointer(true),
+							},
 						},
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
diff --git a/pkg/provider/kubernetes/ingress/annotations_test.go b/pkg/provider/kubernetes/ingress/annotations_test.go
index 61c93061f8..8f011e4163 100644
--- a/pkg/provider/kubernetes/ingress/annotations_test.go
+++ b/pkg/provider/kubernetes/ingress/annotations_test.go
@@ -18,20 +18,23 @@ func Test_parseRouterConfig(t *testing.T) {
 		{
 			desc: "router annotations",
 			annotations: map[string]string{
-				"ingress.kubernetes.io/foo":                               "bar",
-				"traefik.ingress.kubernetes.io/foo":                       "bar",
-				"traefik.ingress.kubernetes.io/router.pathmatcher":        "foobar",
-				"traefik.ingress.kubernetes.io/router.entrypoints":        "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.middlewares":        "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.priority":           "42",
-				"traefik.ingress.kubernetes.io/router.rulesyntax":         "foobar",
-				"traefik.ingress.kubernetes.io/router.tls":                "true",
-				"traefik.ingress.kubernetes.io/router.tls.certresolver":   "foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.0.main": "foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.0.sans": "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.1.main": "foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.1.sans": "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.tls.options":        "foobar",
+				"ingress.kubernetes.io/foo":                                     "bar",
+				"traefik.ingress.kubernetes.io/foo":                             "bar",
+				"traefik.ingress.kubernetes.io/router.pathmatcher":              "foobar",
+				"traefik.ingress.kubernetes.io/router.entrypoints":              "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.middlewares":              "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.priority":                 "42",
+				"traefik.ingress.kubernetes.io/router.rulesyntax":               "foobar",
+				"traefik.ingress.kubernetes.io/router.tls":                      "true",
+				"traefik.ingress.kubernetes.io/router.tls.certresolver":         "foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.0.main":       "foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.0.sans":       "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.1.main":       "foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.1.sans":       "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.tls.options":              "foobar",
+				"traefik.ingress.kubernetes.io/router.observability.accessLogs": "true",
+				"traefik.ingress.kubernetes.io/router.observability.metrics":    "true",
+				"traefik.ingress.kubernetes.io/router.observability.tracing":    "true",
 			},
 			expected: &RouterConfig{
 				Router: &RouterIng{
@@ -54,6 +57,11 @@ func Test_parseRouterConfig(t *testing.T) {
 						},
 						Options: "foobar",
 					},
+					Observability: &dynamic.RouterObservabilityConfig{
+						AccessLogs: pointer(true),
+						Tracing:    pointer(true),
+						Metrics:    pointer(true),
+					},
 				},
 			},
 		},
@@ -182,35 +190,41 @@ func Test_convertAnnotations(t *testing.T) {
 		{
 			desc: "router annotations",
 			annotations: map[string]string{
-				"ingress.kubernetes.io/foo":                               "bar",
-				"traefik.ingress.kubernetes.io/foo":                       "bar",
-				"traefik.ingress.kubernetes.io/router.pathmatcher":        "foobar",
-				"traefik.ingress.kubernetes.io/router.entrypoints":        "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.middlewares":        "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.priority":           "42",
-				"traefik.ingress.kubernetes.io/router.rulesyntax":         "foobar",
-				"traefik.ingress.kubernetes.io/router.tls":                "true",
-				"traefik.ingress.kubernetes.io/router.tls.certresolver":   "foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.0.main": "foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.0.sans": "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.1.main": "foobar",
-				"traefik.ingress.kubernetes.io/router.tls.domains.1.sans": "foobar,foobar",
-				"traefik.ingress.kubernetes.io/router.tls.options":        "foobar",
+				"ingress.kubernetes.io/foo":                                     "bar",
+				"traefik.ingress.kubernetes.io/foo":                             "bar",
+				"traefik.ingress.kubernetes.io/router.pathmatcher":              "foobar",
+				"traefik.ingress.kubernetes.io/router.entrypoints":              "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.middlewares":              "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.priority":                 "42",
+				"traefik.ingress.kubernetes.io/router.rulesyntax":               "foobar",
+				"traefik.ingress.kubernetes.io/router.tls":                      "true",
+				"traefik.ingress.kubernetes.io/router.tls.certresolver":         "foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.0.main":       "foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.0.sans":       "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.1.main":       "foobar",
+				"traefik.ingress.kubernetes.io/router.tls.domains.1.sans":       "foobar,foobar",
+				"traefik.ingress.kubernetes.io/router.tls.options":              "foobar",
+				"traefik.ingress.kubernetes.io/router.observability.accessLogs": "true",
+				"traefik.ingress.kubernetes.io/router.observability.metrics":    "true",
+				"traefik.ingress.kubernetes.io/router.observability.tracing":    "true",
 			},
 			expected: map[string]string{
-				"traefik.foo":                        "bar",
-				"traefik.router.pathmatcher":         "foobar",
-				"traefik.router.entrypoints":         "foobar,foobar",
-				"traefik.router.middlewares":         "foobar,foobar",
-				"traefik.router.priority":            "42",
-				"traefik.router.rulesyntax":          "foobar",
-				"traefik.router.tls":                 "true",
-				"traefik.router.tls.certresolver":    "foobar",
-				"traefik.router.tls.domains[0].main": "foobar",
-				"traefik.router.tls.domains[0].sans": "foobar,foobar",
-				"traefik.router.tls.domains[1].main": "foobar",
-				"traefik.router.tls.domains[1].sans": "foobar,foobar",
-				"traefik.router.tls.options":         "foobar",
+				"traefik.foo":                             "bar",
+				"traefik.router.pathmatcher":              "foobar",
+				"traefik.router.entrypoints":              "foobar,foobar",
+				"traefik.router.middlewares":              "foobar,foobar",
+				"traefik.router.priority":                 "42",
+				"traefik.router.rulesyntax":               "foobar",
+				"traefik.router.tls":                      "true",
+				"traefik.router.tls.certresolver":         "foobar",
+				"traefik.router.tls.domains[0].main":      "foobar",
+				"traefik.router.tls.domains[0].sans":      "foobar,foobar",
+				"traefik.router.tls.domains[1].main":      "foobar",
+				"traefik.router.tls.domains[1].sans":      "foobar,foobar",
+				"traefik.router.tls.options":              "foobar",
+				"traefik.router.observability.accessLogs": "true",
+				"traefik.router.observability.metrics":    "true",
+				"traefik.router.observability.tracing":    "true",
 			},
 		},
 		{
diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go
index ff54a39226..bbfeea77f4 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes_test.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go
@@ -115,6 +115,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 								},
 								Options: "foobar",
 							},
+							Observability: &dynamic.RouterObservabilityConfig{
+								AccessLogs: pointer(true),
+								Tracing:    pointer(true),
+								Metrics:    pointer(true),
+							},
 						},
 					},
 					Services: map[string]*dynamic.Service{
diff --git a/pkg/provider/traefik/internal_test.go b/pkg/provider/traefik/internal_test.go
index c8d64f6be1..ed7197f827 100644
--- a/pkg/provider/traefik/internal_test.go
+++ b/pkg/provider/traefik/internal_test.go
@@ -184,6 +184,11 @@ func Test_createConfiguration(t *testing.T) {
 								},
 							},
 						},
+						Observability: &static.ObservabilityConfig{
+							AccessLogs: false,
+							Tracing:    false,
+							Metrics:    false,
+						},
 					},
 				},
 			},
diff --git a/pkg/redactor/redactor_config_test.go b/pkg/redactor/redactor_config_test.go
index f26acd329b..b086ccd6a0 100644
--- a/pkg/redactor/redactor_config_test.go
+++ b/pkg/redactor/redactor_config_test.go
@@ -57,6 +57,11 @@ func init() {
 						},
 					},
 				},
+				Observability: &dynamic.RouterObservabilityConfig{
+					AccessLogs: pointer(true),
+					Tracing:    pointer(true),
+					Metrics:    pointer(true),
+				},
 			},
 		},
 		Services: map[string]*dynamic.Service{
diff --git a/pkg/redactor/testdata/anonymized-dynamic-config.json b/pkg/redactor/testdata/anonymized-dynamic-config.json
index ed3c07c860..4b71f1c76b 100644
--- a/pkg/redactor/testdata/anonymized-dynamic-config.json
+++ b/pkg/redactor/testdata/anonymized-dynamic-config.json
@@ -22,6 +22,11 @@
               ]
             }
           ]
+        },
+        "observability": {
+          "accessLogs": true,
+          "tracing": true,
+          "metrics": true
         }
       }
     },
@@ -327,7 +332,8 @@
               ]
             }
           ]
-        }
+        },
+        "observability": {}
       }
     },
     "serversTransports": {
diff --git a/pkg/redactor/testdata/secured-dynamic-config.json b/pkg/redactor/testdata/secured-dynamic-config.json
index 75c70ae25e..c9674639b4 100644
--- a/pkg/redactor/testdata/secured-dynamic-config.json
+++ b/pkg/redactor/testdata/secured-dynamic-config.json
@@ -22,6 +22,11 @@
               ]
             }
           ]
+        },
+        "observability": {
+          "accessLogs": true,
+          "tracing": true,
+          "metrics": true
         }
       }
     },
@@ -330,7 +335,8 @@
               ]
             }
           ]
-        }
+        },
+        "observability": {}
       }
     },
     "serversTransports": {
diff --git a/pkg/server/aggregator_test.go b/pkg/server/aggregator_test.go
index b70d261aea..50a400d2ac 100644
--- a/pkg/server/aggregator_test.go
+++ b/pkg/server/aggregator_test.go
@@ -9,6 +9,8 @@ import (
 	"github.com/traefik/traefik/v3/pkg/tls"
 )
 
+func pointer[T any](v T) *T { return &v }
+
 func Test_mergeConfiguration(t *testing.T) {
 	testCases := []struct {
 		desc     string
@@ -555,12 +557,62 @@ func Test_applyModel(t *testing.T) {
 				},
 			},
 			expected: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"test": {
+							EntryPoints:   []string{"websecure"},
+							Middlewares:   []string{"test"},
+							TLS:           &dynamic.RouterTLSConfig{},
+							Observability: &dynamic.RouterObservabilityConfig{},
+						},
+					},
+					Middlewares: make(map[string]*dynamic.Middleware),
+					Services:    make(map[string]*dynamic.Service),
+					Models: map[string]*dynamic.Model{
+						"websecure@internal": {
+							Middlewares: []string{"test"},
+							TLS:         &dynamic.RouterTLSConfig{},
+						},
+					},
+				},
+			},
+		},
+		{
+			desc: "with model, one entry point with observability",
+			input: dynamic.Configuration{
 				HTTP: &dynamic.HTTPConfiguration{
 					Routers: map[string]*dynamic.Router{
 						"test": {
 							EntryPoints: []string{"websecure"},
+						},
+					},
+					Middlewares: make(map[string]*dynamic.Middleware),
+					Services:    make(map[string]*dynamic.Service),
+					Models: map[string]*dynamic.Model{
+						"websecure@internal": {
 							Middlewares: []string{"test"},
 							TLS:         &dynamic.RouterTLSConfig{},
+							Observability: dynamic.RouterObservabilityConfig{
+								AccessLogs: pointer(true),
+								Tracing:    pointer(true),
+								Metrics:    pointer(true),
+							},
+						},
+					},
+				},
+			},
+			expected: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"test": {
+							EntryPoints: []string{"websecure"},
+							Middlewares: []string{"test"},
+							TLS:         &dynamic.RouterTLSConfig{},
+							Observability: &dynamic.RouterObservabilityConfig{
+								AccessLogs: pointer(true),
+								Tracing:    pointer(true),
+								Metrics:    pointer(true),
+							},
 						},
 					},
 					Middlewares: make(map[string]*dynamic.Middleware),
@@ -569,6 +621,11 @@ func Test_applyModel(t *testing.T) {
 						"websecure@internal": {
 							Middlewares: []string{"test"},
 							TLS:         &dynamic.RouterTLSConfig{},
+							Observability: dynamic.RouterObservabilityConfig{
+								AccessLogs: pointer(true),
+								Tracing:    pointer(true),
+								Metrics:    pointer(true),
+							},
 						},
 					},
 				},
@@ -601,6 +658,11 @@ func Test_applyModel(t *testing.T) {
 							EntryPoints: []string{"websecure"},
 							Middlewares: []string{"test"},
 							TLS:         &dynamic.RouterTLSConfig{CertResolver: "router"},
+							Observability: &dynamic.RouterObservabilityConfig{
+								AccessLogs: nil,
+								Tracing:    nil,
+								Metrics:    nil,
+							},
 						},
 					},
 					Middlewares: make(map[string]*dynamic.Middleware),
@@ -640,9 +702,10 @@ func Test_applyModel(t *testing.T) {
 							EntryPoints: []string{"web"},
 						},
 						"websecure-test": {
-							EntryPoints: []string{"websecure"},
-							Middlewares: []string{"test"},
-							TLS:         &dynamic.RouterTLSConfig{},
+							EntryPoints:   []string{"websecure"},
+							Middlewares:   []string{"test"},
+							TLS:           &dynamic.RouterTLSConfig{},
+							Observability: &dynamic.RouterObservabilityConfig{},
 						},
 					},
 					Middlewares: make(map[string]*dynamic.Middleware),