Repository: containous/traefik
Pull request #5841 (2019-11-15 12:37:15)

Merge current v2.0 branch into master
### What does this PR do?

Merge current v2.0 branch into master 

### Motivation

Be sync.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 96f36e5ed..7cec1b7d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,24 @@
+## [v2.0.5](https://github.com/containous/traefik/tree/v2.0.5) (2019-11-14)
+[All Commits](https://github.com/containous/traefik/compare/v2.0.4...v2.0.5)
+
+**Bug fixes:**
+- **[metrics]** fix: metric with services LB. ([#5759](https://github.com/containous/traefik/pull/5759) by [ldez](https://github.com/ldez))
+- **[middleware]** fix: stripPrefix middleware with empty resulting path. ([#5806](https://github.com/containous/traefik/pull/5806) by [ldez](https://github.com/ldez))
+- **[middleware]** Fix rate limiting and SSE ([#5737](https://github.com/containous/traefik/pull/5737) by [sylr](https://github.com/sylr))
+- **[tracing]** Upgrades zipkin library to avoid errors when using textMap. ([#5754](https://github.com/containous/traefik/pull/5754) by [jcchavezs](https://github.com/jcchavezs))
+
+**Documentation:**
+- **[acme,cluster]** Update ACME storage docs to remove reference to KV store in CE ([#5433](https://github.com/containous/traefik/pull/5433) by [bradjones1](https://github.com/bradjones1))
+- **[api]** docs: remove field api.entryPoint ([#5776](https://github.com/containous/traefik/pull/5776) by [waitingsong](https://github.com/waitingsong))
+- **[api]** Adds missed quotes in api.md ([#5787](https://github.com/containous/traefik/pull/5787) by [woto](https://github.com/woto))
+- **[docker/swarm]** Dashboard example with swarm ([#5795](https://github.com/containous/traefik/pull/5795) by [dduportal](https://github.com/dduportal))
+- **[docker]** Fix error in link description for priority ([#5746](https://github.com/containous/traefik/pull/5746) by [ASDFGamer](https://github.com/ASDFGamer))
+- **[k8s]** Wrong endpoint on the TLS secret example ([#5817](https://github.com/containous/traefik/pull/5817) by [yacinelazaar](https://github.com/yacinelazaar))
+- **[middleware,docker]** Double dollar on docker-compose config ([#5775](https://github.com/containous/traefik/pull/5775) by [clery](https://github.com/clery))
+- Fix quickstart link in README ([#5794](https://github.com/containous/traefik/pull/5794) by [mcky](https://github.com/mcky))
+- fix typo in v1 to v2 migration guide ([#5820](https://github.com/containous/traefik/pull/5820) by [fschl](https://github.com/fschl))
+- slashes ended up in bad place. ([#5798](https://github.com/containous/traefik/pull/5798) by [icepic](https://github.com/icepic))
+
 ## [v2.0.4](https://github.com/containous/traefik/tree/v2.0.4) (2019-10-28)
 [All Commits](https://github.com/containous/traefik/compare/v2.0.3...v2.0.4)
 
diff --git a/README.md b/README.md
index 4c7574e8e..ee973b0ea 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
 
 ## Quickstart
 
-To get your hands on Traefik, you can use the [5-Minute Quickstart](http://docs.traefik.io/#the-traefik-quickstart-using-docker) in our documentation (you will need Docker).
+To get your hands on Traefik, you can use the [5-Minute Quickstart](https://docs.traefik.io/getting-started/quick-start/) in our documentation (you will need Docker).
 
 ## Web UI
 
diff --git a/docs/.markdownlint.json b/docs/.markdownlint.json
index c10d547d3..79b1ce5ec 100644
--- a/docs/.markdownlint.json
+++ b/docs/.markdownlint.json
@@ -7,5 +7,6 @@
     "MD026": false,
     "MD033": false,
     "MD034": false,
-    "MD036": false
+    "MD036": false,
+    "MD046": false
 }
diff --git a/docs/check.Dockerfile b/docs/check.Dockerfile
index bbc51e6b4..79af5c3cf 100644
--- a/docs/check.Dockerfile
+++ b/docs/check.Dockerfile
@@ -1,9 +1,6 @@
 
-FROM alpine:3.9 as alpine
+FROM alpine:3.10 as alpine
 
-# The "build-dependencies" virtual package provides build tools for html-proofer installation.
-# It compile ruby-nokogiri, because alpine native version is always out of date
-# This virtual package is cleaned at the end.
 RUN apk --no-cache --no-progress add \
     libcurl \
     ruby \
@@ -11,21 +8,17 @@ RUN apk --no-cache --no-progress add \
     ruby-etc \
     ruby-ffi \
     ruby-json \
-  && apk add --no-cache --virtual build-dependencies \
-    build-base \
-    libcurl \
-    libxml2-dev \
-    libxslt-dev \
-    ruby-dev \
-  && gem install --no-document html-proofer -v 3.10.2 \
-  && apk del build-dependencies
+    ruby-nokogiri
+RUN gem install html-proofer --version 3.13.0 --no-document -- --use-system-libraries
 
 # After Ruby, some NodeJS YAY!
 RUN apk --no-cache --no-progress add \
     git \
     nodejs \
     npm \
-  && npm install markdownlint@0.12.0 markdownlint-cli@0.13.0 --global
+  && npm install --global \
+    markdownlint@0.17.2 \
+    markdownlint-cli@0.19.0
 
 # Finally the shell tools we need for later
 # tini helps to terminate properly all the parallelized tasks when sending CTRL-C
diff --git a/docs/content/contributing/submitting-security-issues.md b/docs/content/contributing/submitting-security-issues.md
new file mode 100644
index 000000000..be3187ed1
--- /dev/null
+++ b/docs/content/contributing/submitting-security-issues.md
@@ -0,0 +1,16 @@
+# Security
+
+## Security Advisories
+
+We strongly advise you to join our mailing list to be aware of the latest announcements from our security team.
+You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
+
+## CVE
+
+Reported vulnerabilities can be found on 
+[cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
+
+## Report a Vulnerability
+
+We want to keep Traefik safe for everyone.
+If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).
diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md
index 039c54dd2..80e48d1d6 100644
--- a/docs/content/https/acme.md
+++ b/docs/content/https/acme.md
@@ -215,6 +215,7 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
 | [ACME DNS](https://github.com/joohoi/acme-dns)              | `acme-dns`     | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH`                                                                                                | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns)     |
 | [Alibaba Cloud](https://www.alibabacloud.com)               | `alidns`       | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID`                                                                          | [Additional configuration](https://go-acme.github.io/lego/dns/alidns)       |
 | [Auroradns](https://www.pcextreme.com/aurora/dns)           | `auroradns`    | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT`                                                                                           | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns)    |
+| [Autodns](https://www.internetx.com/domains/autodns/)       | `autodns`      | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD`                                                                                                  | [Additional configuration](https://go-acme.github.io/lego/dns/autodns)      |
 | [Azure](https://azure.microsoft.com/services/dns/)          | `azure`        | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]`   | [Additional configuration](https://go-acme.github.io/lego/dns/azure)        |
 | [Bindman](https://github.com/labbsr0x/bindman-dns-webhook)  | `bindman`      | `BINDMAN_MANAGER_ADDRESS`                                                                                                                   | [Additional configuration](https://go-acme.github.io/lego/dns/bindman)      |
 | [Blue Cat](https://www.bluecatnetworks.com/)                | `bluecat`      | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW`                                    | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat)      |
@@ -390,7 +391,7 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
 ```
 
 !!! warning
-    For concurrency reason, this file cannot be shared across multiple instances of Traefik. Use a key value store entry instead.
+    For concurrency reason, this file cannot be shared across multiple instances of Traefik.
 
 ## Fallback
 
diff --git a/docs/content/middlewares/addprefix.md b/docs/content/middlewares/addprefix.md
index 909c11335..fbcb92bd5 100644
--- a/docs/content/middlewares/addprefix.md
+++ b/docs/content/middlewares/addprefix.md
@@ -63,4 +63,5 @@ http:
 
 ### `prefix`
 
-`prefix` is the string to add before the current path in the requested URL. It should include the leading slash (`/`).
+`prefix` is the string to add before the current path in the requested URL.
+It should include the leading slash (`/`).
diff --git a/docs/content/middlewares/replacepathregex.md b/docs/content/middlewares/replacepathregex.md
index b1037a316..d62843dff 100644
--- a/docs/content/middlewares/replacepathregex.md
+++ b/docs/content/middlewares/replacepathregex.md
@@ -15,7 +15,7 @@ The ReplaceRegex replace a path from an url to another with regex matching and r
 # Replace path with regex
 labels:
   - "traefik.http.middlewares.test-replacepathregex.replacepathregex.regex=^/foo/(.*)"
-  - "traefik.http.middlewares.test-replacepathregex.replacepathregex.replacement=/bar/$1"
+  - "traefik.http.middlewares.test-replacepathregex.replacepathregex.replacement=/bar/$$1"
 ```
 
 ```yaml tab="Kubernetes"
diff --git a/docs/content/middlewares/stripprefix.md b/docs/content/middlewares/stripprefix.md
index 1390ae209..32eaa3eb8 100644
--- a/docs/content/middlewares/stripprefix.md
+++ b/docs/content/middlewares/stripprefix.md
@@ -90,3 +90,85 @@ If your backend is serving assets (e.g., images or Javascript files), chances ar
 Continuing on the example, the backend should return `/products/shoes/image.png` (and not `/images.png` which Traefik would likely not be able to associate with the same backend).  
 
 The `X-Forwarded-Prefix` header can be queried to build such URLs dynamically.
+
+### `forceSlash`
+
+_Optional, Default=true_
+
+```yaml tab="Docker"
+labels:
+  - "traefik.http.middlewares.example.stripprefix.prefixes=/foobar"
+  - "traefik.http.middlewares.example.stripprefix.forceslash=false"
+```
+
+```yaml tab="Kubernetes"
+apiVersion: traefik.containo.us/v1alpha1
+kind: Middleware
+metadata:
+  name: example
+spec:
+  stripPrefix:
+    prefixes:
+      - "/foobar"
+    forceSlash: false
+```
+
+```json tab="Marathon"
+"labels": {
+  "traefik.http.middlewares.example.stripprefix.prefixes": "/foobar",
+  "traefik.http.middlewares.example.stripprefix.forceslash": "false"
+}
+```
+
+```yaml tab="Rancher"
+labels:
+  - "traefik.http.middlewares.example.stripprefix.prefixes=/foobar"
+  - "traefik.http.middlewares.example.stripprefix.forceSlash=false"
+```
+
+```toml tab="File (TOML)"
+[http.middlewares]
+  [http.middlewares.example.stripPrefix]
+    prefixes = ["/foobar"]
+    forceSlash = false
+```
+
+```yaml tab="File (YAML)"
+http:
+  middlewares:
+    example:
+      stripPrefix:
+        prefixes:
+          - "/foobar"
+        forceSlash: false
+```
+
+The `forceSlash` option makes sure that the resulting stripped path is not the empty string, by replacing it with `/` when necessary.
+
+This option was added to keep the initial (non-intuitive) behavior of this middleware, in order to avoid introducing a breaking change.
+
+It's recommended to explicitly set `forceSlash` to `false`.
+
+??? info "Behavior examples"
+    
+    - `forceSlash=true`
+    
+    | Path       | Prefix to strip | Result |
+    |------------|-----------------|--------|
+    | `/`        | `/`             | `/`    |
+    | `/foo`     | `/foo`          | `/`    |
+    | `/foo/`    | `/foo`          | `/`    |
+    | `/foo/`    | `/foo/`         | `/`    |
+    | `/bar`     | `/foo`          | `/bar` |
+    | `/foo/bar` | `/foo`          | `/bar` |
+    
+    - `forceSlash=false`
+    
+    | Path       | Prefix to strip | Result |
+    |------------|-----------------|--------|
+    | `/`        | `/`             | empty  |
+    | `/foo`     | `/foo`          | empty  |
+    | `/foo/`    | `/foo`          | `/`    |
+    | `/foo/`    | `/foo/`         | empty  |
+    | `/bar`     | `/foo`          | `/bar` |
+    | `/foo/bar` | `/foo`          | `/bar` |
diff --git a/docs/content/migration/v1-to-v2.md b/docs/content/migration/v1-to-v2.md
index 0a871e709..287981564 100644
--- a/docs/content/migration/v1-to-v2.md
+++ b/docs/content/migration/v1-to-v2.md
@@ -519,7 +519,7 @@ Use Case: Incoming requests to `http://company.org/admin` are forwarded to the w
 with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, you must:
 
 * First, configure a router named `admin` with a rule matching at least the path prefix with the `PathPrefix` keyword,
-* Then, define a middlware of type [`stripprefix`](../../middlewares/stripprefix/), which remove the prefix `/admin`, associated to the router `admin`.
+* Then, define a middleware of type [`stripprefix`](../../middlewares/stripprefix/), which remove the prefix `/admin`, associated to the router `admin`.
 
 !!! example "Strip Path Prefix When Forwarding to Backend"
 
@@ -974,7 +974,7 @@ You need to activate the API to access the [dashboard](../operations/dashboard.m
 As the dashboard access is now secured by default you can either:
 
 * define a  [specific router](../operations/api.md#configuration) with the `api@internal` service and one authentication middleware like the following example
-* or use the [unsecure](../operations/api.md#insecure) option of the API
+* or use the [insecure](../operations/api.md#insecure) option of the API
 
 !!! info "Dashboard with k8s and dedicated router"
 
diff --git a/docs/content/operations/.markdownlint.json b/docs/content/operations/.markdownlint.json
index 3cd5e9a07..e404a3618 100644
--- a/docs/content/operations/.markdownlint.json
+++ b/docs/content/operations/.markdownlint.json
@@ -1,4 +1,5 @@
 {
     "extends": "../../.markdownlint.json",
+    "MD041": false,
     "MD046": false
 }
diff --git a/docs/content/operations/api.md b/docs/content/operations/api.md
index 4b9391b39..b8a3c3391 100644
--- a/docs/content/operations/api.md
+++ b/docs/content/operations/api.md
@@ -43,71 +43,7 @@ api: {}
 And then define a routing configuration on Traefik itself with the
 [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration):
 
-```yaml tab="Docker"
-# Dynamic Configuration
-labels:
-  - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
-  - "traefik.http.routers.api.service=api@internal"
-  - "traefik.http.routers.api.middlewares=auth"
-  - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
-```
-
-```yaml tab="Consul Catalog"
-# Declaring the user list
-- "traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
-- "traefik.http.routers.api.service=api@internal"
-- "traefik.http.routers.api.middlewares=auth"
-- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
-```
-
-```json tab="Marathon"
-"labels": {
-  "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)",
-  "traefik.http.routers.api.service": "api@internal",
-  "traefik.http.routers.api.middlewares": "auth",
-  "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
-}
-```
-
-```yaml tab="Rancher"
-# Dynamic Configuration
-labels:
-  - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
-  - "traefik.http.routers.api.service=api@internal"
-  - "traefik.http.routers.api.middlewares=auth"
-  - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
-```
-
-```toml tab="File (TOML)"
-# Dynamic Configuration
-[http.routers.my-api]
-    rule="Host(`traefik.domain.com`)
-    service="api@internal"
-    middlewares=["auth"]
-
-[http.middlewares.auth.basicAuth]
-    users = [
-      "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
-      "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
-    ]
-```
-
-```yaml tab="File (YAML)"
-# Dynamic Configuration
-http:
-  routers:
-    api:
-      rule: Host(`traefik.domain.com`)
-      service: api@internal
-      middlewares:
-        - auth
-  middlewares:
-    auth:
-      basicAuth:
-        users:
-          - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
-          - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
-```
+--8<-- "content/operations/include-api-examples.md"
 
 ??? warning "The router's [rule](../../routing/routers#rule) must catch requests for the URI path `/api`"
     Using an "Host" rule is recommended, by catching all the incoming traffic on this host domain to the API.
diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md
index 169e136f2..46fbb9775 100644
--- a/docs/content/operations/dashboard.md
+++ b/docs/content/operations/dashboard.md
@@ -60,8 +60,8 @@ api:
 --api.dashboard=true
 ```
 
-Then define a routing configuration on Traefik itself, 
-with a router attached to the service `api@internal` in the 
+Then define a routing configuration on Traefik itself,
+with a router attached to the service `api@internal` in the
 [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration),
 to allow defining:
 
@@ -73,64 +73,7 @@ to allow defining:
   through Traefik itself (sometimes referred as "Traefik-ception").
 
 ??? example "Dashboard Dynamic Configuration Examples"
-
-    ```yaml tab="Docker"
-    # Dynamic Configuration
-    labels:
-      - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
-      - "traefik.http.routers.api.service=api@internal"
-      - "traefik.http.routers.api.middlewares=auth"
-      - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
-    ```
-
-    ```json tab="Marathon"
-    "labels": {
-      "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)",
-      "traefik.http.routers.api.service": "api@internal",
-      "traefik.http.routers.api.middlewares": "auth",
-      "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
-    }
-    ```
-
-    ```yaml tab="Rancher"
-    # Dynamic Configuration
-    labels:
-      - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
-      - "traefik.http.routers.api.service=api@internal"
-      - "traefik.http.routers.api.middlewares=auth"
-      - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
-    ```
-
-    ```toml tab="File (TOML)"
-    # Dynamic Configuration
-    [http.routers.my-api]
-        rule="Host(`traefik.domain.com`)
-        service="api@internal"
-        middlewares=["auth"]
-
-    [http.middlewares.auth.basicAuth]
-        users = [
-          "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
-          "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
-        ]
-    ```
-
-    ```yaml tab="File (YAML)"
-    # Dynamic Configuration
-    http:
-      routers:
-        api:
-          rule: Host(`traefik.domain.com`)
-          service: api@internal
-          middlewares:
-            - auth
-      middlewares:
-        auth:
-          basicAuth:
-            users:
-              - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
-              - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
-    ```
+    --8<-- "content/operations/include-api-examples.md"
 
 ### Dashboard Router Rule
 
diff --git a/docs/content/operations/include-api-examples.md b/docs/content/operations/include-api-examples.md
new file mode 100644
index 000000000..b16da30f6
--- /dev/null
+++ b/docs/content/operations/include-api-examples.md
@@ -0,0 +1,77 @@
+```yaml tab="Docker"
+# Dynamic Configuration
+labels:
+  - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)"
+  - "traefik.http.routers.api.service=api@internal"
+  - "traefik.http.routers.api.middlewares=auth"
+  - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
+```
+
+```yaml tab="Docker (Swarm)"
+# Dynamic Configuration
+deploy:
+  labels:
+    - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)"
+    - "traefik.http.routers.api.service=api@internal"
+    - "traefik.http.routers.api.middlewares=auth"
+    - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
+    # Dummy service for Swarm port detection. The port can be any valid integer value.
+    - "traefik.http.services.dummy-svc.loadbalancer.server.port=9999"
+```
+
+```yaml tab="Consul Catalog"
+# Dynamic Configuration
+- "traefik.http.routers.api.rule=Host(`traefik.domain.com`)"
+- "traefik.http.routers.api.service=api@internal"
+- "traefik.http.routers.api.middlewares=auth"
+- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
+```
+
+```json tab="Marathon"
+"labels": {
+  "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)",
+  "traefik.http.routers.api.service": "api@internal",
+  "traefik.http.routers.api.middlewares": "auth",
+  "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
+}
+```
+
+```yaml tab="Rancher"
+# Dynamic Configuration
+labels:
+  - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)"
+  - "traefik.http.routers.api.service=api@internal"
+  - "traefik.http.routers.api.middlewares=auth"
+  - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
+```
+
+```toml tab="File (TOML)"
+# Dynamic Configuration
+[http.routers.my-api]
+  rule = "Host(`traefik.domain.com`)"
+  service = "api@internal"
+  middlewares = ["auth"]
+
+[http.middlewares.auth.basicAuth]
+  users = [
+    "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
+    "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
+  ]
+```
+
+```yaml tab="File (YAML)"
+# Dynamic Configuration
+http:
+  routers:
+    api:
+      rule: Host(`traefik.domain.com`)
+      service: api@internal
+      middlewares:
+        - auth
+  middlewares:
+    auth:
+      basicAuth:
+        users:
+          - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
+          - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
+```
diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml
index 722a4fdf4..19ce7cef5 100644
--- a/docs/content/reference/dynamic-configuration/docker-labels.yml
+++ b/docs/content/reference/dynamic-configuration/docker-labels.yml
@@ -103,6 +103,7 @@
 - "traefik.http.middlewares.middleware17.replacepathregex.regex=foobar"
 - "traefik.http.middlewares.middleware17.replacepathregex.replacement=foobar"
 - "traefik.http.middlewares.middleware18.retry.attempts=42"
+- "traefik.http.middlewares.middleware19.stripprefix.forceslash=true"
 - "traefik.http.middlewares.middleware19.stripprefix.prefixes=foobar, foobar"
 - "traefik.http.middlewares.middleware20.stripprefixregex.regex=foobar, foobar"
 - "traefik.http.routers.router0.entrypoints=foobar, foobar"
@@ -129,38 +130,22 @@
 - "traefik.http.routers.router1.tls.domains[1].main=foobar"
 - "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar"
 - "traefik.http.routers.router1.tls.options=foobar"
-- "traefik.http.services.service0.loadbalancer.healthcheck.headers.name0=foobar"
-- "traefik.http.services.service0.loadbalancer.healthcheck.headers.name1=foobar"
-- "traefik.http.services.service0.loadbalancer.healthcheck.hostname=foobar"
-- "traefik.http.services.service0.loadbalancer.healthcheck.interval=foobar"
-- "traefik.http.services.service0.loadbalancer.healthcheck.path=foobar"
-- "traefik.http.services.service0.loadbalancer.healthcheck.port=42"
-- "traefik.http.services.service0.loadbalancer.healthcheck.scheme=foobar"
-- "traefik.http.services.service0.loadbalancer.healthcheck.timeout=foobar"
-- "traefik.http.services.service0.loadbalancer.passhostheader=true"
-- "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval=foobar"
-- "traefik.http.services.service0.loadbalancer.sticky=true"
-- "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly=true"
-- "traefik.http.services.service0.loadbalancer.sticky.cookie.name=foobar"
-- "traefik.http.services.service0.loadbalancer.sticky.cookie.secure=true"
-- "traefik.http.services.service0.loadbalancer.server.port=foobar"
-- "traefik.http.services.service0.loadbalancer.server.scheme=foobar"
-- "traefik.http.services.service1.loadbalancer.healthcheck.headers.name0=foobar"
-- "traefik.http.services.service1.loadbalancer.healthcheck.headers.name1=foobar"
-- "traefik.http.services.service1.loadbalancer.healthcheck.hostname=foobar"
-- "traefik.http.services.service1.loadbalancer.healthcheck.interval=foobar"
-- "traefik.http.services.service1.loadbalancer.healthcheck.path=foobar"
-- "traefik.http.services.service1.loadbalancer.healthcheck.port=42"
-- "traefik.http.services.service1.loadbalancer.healthcheck.scheme=foobar"
-- "traefik.http.services.service1.loadbalancer.healthcheck.timeout=foobar"
-- "traefik.http.services.service1.loadbalancer.passhostheader=true"
-- "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval=foobar"
-- "traefik.http.services.service1.loadbalancer.sticky=true"
-- "traefik.http.services.service1.loadbalancer.sticky.cookie.httponly=true"
-- "traefik.http.services.service1.loadbalancer.sticky.cookie.name=foobar"
-- "traefik.http.services.service1.loadbalancer.sticky.cookie.secure=true"
-- "traefik.http.services.service1.loadbalancer.server.port=foobar"
-- "traefik.http.services.service1.loadbalancer.server.scheme=foobar"
+- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0=foobar"
+- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1=foobar"
+- "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar"
+- "traefik.http.services.service01.loadbalancer.healthcheck.interval=foobar"
+- "traefik.http.services.service01.loadbalancer.healthcheck.path=foobar"
+- "traefik.http.services.service01.loadbalancer.healthcheck.port=42"
+- "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar"
+- "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar"
+- "traefik.http.services.service01.loadbalancer.passhostheader=true"
+- "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar"
+- "traefik.http.services.service01.loadbalancer.sticky=true"
+- "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly=true"
+- "traefik.http.services.service01.loadbalancer.sticky.cookie.name=foobar"
+- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true"
+- "traefik.http.services.service01.loadbalancer.server.port=foobar"
+- "traefik.http.services.service01.loadbalancer.server.scheme=foobar"
 - "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"
 - "traefik.tcp.routers.tcprouter0.rule=foobar"
 - "traefik.tcp.routers.tcprouter0.service=foobar"
@@ -183,7 +168,5 @@
 - "traefik.tcp.routers.tcprouter1.tls.domains[1].sans=foobar, foobar"
 - "traefik.tcp.routers.tcprouter1.tls.options=foobar"
 - "traefik.tcp.routers.tcprouter1.tls.passthrough=true"
-- "traefik.tcp.services.tcpservice0.loadbalancer.server.port=foobar"
-- "traefik.tcp.services.tcpservice0.loadbalancer.terminationdelay=100"
-- "traefik.tcp.services.tcpservice1.loadbalancer.server.port=foobar"
-- "traefik.tcp.services.tcpservice1.loadbalancer.terminationdelay=100"
+- "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay=42"
+- "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar"
diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml
index 37c16d0f8..5b6889254 100644
--- a/docs/content/reference/dynamic-configuration/file.toml
+++ b/docs/content/reference/dynamic-configuration/file.toml
@@ -245,6 +245,7 @@
     [http.middlewares.Middleware19]
       [http.middlewares.Middleware19.stripPrefix]
         prefixes = ["foobar", "foobar"]
+        forceSlash = true
     [http.middlewares.Middleware20]
       [http.middlewares.Middleware20.stripPrefixRegex]
         regex = ["foobar", "foobar"]
@@ -284,25 +285,25 @@
           main = "foobar"
           sans = ["foobar", "foobar"]
   [tcp.services]
-    [tcp.services.TCPService0]
-      [tcp.services.TCPService0.loadBalancer]
-        terminationDelay = 100
+    [tcp.services.TCPService01]
+      [tcp.services.TCPService01.loadBalancer]
+        terminationDelay = 42
 
-        [[tcp.services.TCPService0.loadBalancer.servers]]
+        [[tcp.services.TCPService01.loadBalancer.servers]]
           address = "foobar"
 
-        [[tcp.services.TCPService0.loadBalancer.servers]]
+        [[tcp.services.TCPService01.loadBalancer.servers]]
           address = "foobar"
+    [tcp.services.TCPService02]
+      [tcp.services.TCPService02.weighted]
 
-    [tcp.services.TCPService1]
-      [tcp.services.TCPService1.loadBalancer]
-        terminationDelay = 100
-
-        [[tcp.services.TCPService1.loadBalancer.servers]]
-          address = "foobar"
+        [[tcp.services.TCPService02.weighted.services]]
+          name = "foobar"
+          weight = 42
 
-        [[tcp.services.TCPService1.loadBalancer.servers]]
-          address = "foobar"
+        [[tcp.services.TCPService02.weighted.services]]
+          name = "foobar"
+          weight = 42
 
 [tls]
 
diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml
index 49bd65e93..d6e091940 100644
--- a/docs/content/reference/dynamic-configuration/file.yaml
+++ b/docs/content/reference/dynamic-configuration/file.yaml
@@ -2,11 +2,11 @@ http:
   routers:
     Router0:
       entryPoints:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       middlewares:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       service: foobar
       rule: foobar
       priority: 42
@@ -14,21 +14,21 @@ http:
         options: foobar
         certResolver: foobar
         domains:
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
     Router1:
       entryPoints:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       middlewares:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       service: foobar
       rule: foobar
       priority: 42
@@ -36,14 +36,14 @@ http:
         options: foobar
         certResolver: foobar
         domains:
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
   services:
     Service01:
       loadBalancer:
@@ -53,8 +53,8 @@ http:
             secure: true
             httpOnly: true
         servers:
-          - url: foobar
-          - url: foobar
+        - url: foobar
+        - url: foobar
         healthCheck:
           scheme: foobar
           path: foobar
@@ -72,17 +72,17 @@ http:
       mirroring:
         service: foobar
         mirrors:
-          - name: foobar
-            percent: 42
-          - name: foobar
-            percent: 42
+        - name: foobar
+          percent: 42
+        - name: foobar
+          percent: 42
     Service03:
       weighted:
         services:
-          - name: foobar
-            weight: 42
-          - name: foobar
-            weight: 42
+        - name: foobar
+          weight: 42
+        - name: foobar
+          weight: 42
         sticky:
           cookie:
             name: foobar
@@ -95,8 +95,8 @@ http:
     Middleware01:
       basicAuth:
         users:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         usersFile: foobar
         realm: foobar
         removeHeader: true
@@ -111,8 +111,8 @@ http:
     Middleware03:
       chain:
         middlewares:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
     Middleware04:
       circuitBreaker:
         expression: foobar
@@ -121,8 +121,8 @@ http:
     Middleware06:
       digestAuth:
         users:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         usersFile: foobar
         removeHeader: true
         realm: foobar
@@ -130,8 +130,8 @@ http:
     Middleware07:
       errors:
         status:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         service: foobar
         query: foobar
     Middleware08:
@@ -145,8 +145,8 @@ http:
           insecureSkipVerify: true
         trustForwardHeader: true
         authResponseHeaders:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
     Middleware09:
       headers:
         customRequestHeaders:
@@ -157,23 +157,23 @@ http:
           name1: foobar
         accessControlAllowCredentials: true
         accessControlAllowHeaders:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         accessControlAllowMethods:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         accessControlAllowOrigin: foobar
         accessControlExposeHeaders:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         accessControlMaxAge: 42
         addVaryHeader: true
         allowedHosts:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         hostsProxyHeaders:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         sslRedirect: true
         sslTemporaryRedirect: true
         sslHost: foobar
@@ -198,13 +198,13 @@ http:
     Middleware10:
       ipWhiteList:
         sourceRange:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         ipStrategy:
           depth: 42
           excludedIPs:
-            - foobar
-            - foobar
+          - foobar
+          - foobar
     Middleware11:
       inFlightReq:
         amount: 42
@@ -212,8 +212,8 @@ http:
           ipstrategy:
             depth: 42
             excludedIPs:
-              - foobar
-              - foobar
+            - foobar
+            - foobar
           requestHeaderName: foobar
           requestHost: true
     Middleware12:
@@ -247,8 +247,8 @@ http:
           ipstrategy:
             depth: 42
             excludedIPs:
-              - foobar
-              - foobar
+            - foobar
+            - foobar
           requestHeaderName: foobar
           requestHost: true
     Middleware14:
@@ -274,19 +274,20 @@ http:
     Middleware19:
       stripPrefix:
         prefixes:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
+        forceSlash: true
     Middleware20:
       stripPrefixRegex:
         regex:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
 tcp:
   routers:
     TCPRouter0:
       entryPoints:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       service: foobar
       rule: foobar
       tls:
@@ -294,18 +295,18 @@ tcp:
         options: foobar
         certResolver: foobar
         domains:
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
     TCPRouter1:
       entryPoints:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       service: foobar
       rule: foobar
       tls:
@@ -313,39 +314,40 @@ tcp:
         options: foobar
         certResolver: foobar
         domains:
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
-          - main: foobar
-            sans:
-              - foobar
-              - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
+        - main: foobar
+          sans:
+          - foobar
+          - foobar
   services:
-    TCPService0:
+    TCPService01:
       loadBalancer:
-        terminationDelay: 100
+        terminationDelay: 42
         servers:
-          - address: foobar
-          - address: foobar
-    TCPService1:
-      loadBalancer:
-        terminationDelay: 100
-        servers:
-          - address: foobar
-          - address: foobar
+        - address: foobar
+        - address: foobar
+    TCPService02:
+      weighted:
+        services:
+        - name: foobar
+          weight: 42
+        - name: foobar
+          weight: 42
 tls:
   certificates:
-    - certFile: foobar
-      keyFile: foobar
-      stores:
-        - foobar
-        - foobar
-    - certFile: foobar
-      keyFile: foobar
-      stores:
-        - foobar
-        - foobar
+  - certFile: foobar
+    keyFile: foobar
+    stores:
+    - foobar
+    - foobar
+  - certFile: foobar
+    keyFile: foobar
+    stores:
+    - foobar
+    - foobar
   options:
     Options0:
       minVersion: foobar
@@ -354,12 +356,12 @@ tls:
         - foobar
         - foobar
       curvePreferences:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       clientAuth:
         caFiles:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         clientAuthType: foobar
       sniStrict: true
     Options1:
@@ -369,12 +371,12 @@ tls:
         - foobar
         - foobar
       curvePreferences:
-        - foobar
-        - foobar
+      - foobar
+      - foobar
       clientAuth:
         caFiles:
-          - foobar
-          - foobar
+        - foobar
+        - foobar
         clientAuthType: foobar
       sniStrict: true
   stores:
diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json
index 695e8c6bd..4294c82e0 100644
--- a/docs/content/reference/dynamic-configuration/marathon-labels.json
+++ b/docs/content/reference/dynamic-configuration/marathon-labels.json
@@ -103,6 +103,7 @@
 "traefik.http.middlewares.middleware17.replacepathregex.regex": "foobar",
 "traefik.http.middlewares.middleware17.replacepathregex.replacement": "foobar",
 "traefik.http.middlewares.middleware18.retry.attempts": "42",
+"traefik.http.middlewares.middleware19.stripprefix.forceslash": "true",
 "traefik.http.middlewares.middleware19.stripprefix.prefixes": "foobar, foobar",
 "traefik.http.middlewares.middleware20.stripprefixregex.regex": "foobar, foobar",
 "traefik.http.routers.router0.entrypoints": "foobar, foobar",
@@ -110,7 +111,6 @@
 "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.tls.certresolver": "foobar",
 "traefik.http.routers.router0.tls.domains[0].main": "foobar",
 "traefik.http.routers.router0.tls.domains[0].sans": "foobar, foobar",
@@ -122,49 +122,30 @@
 "traefik.http.routers.router1.priority": "42",
 "traefik.http.routers.router1.rule": "foobar",
 "traefik.http.routers.router1.service": "foobar",
-"traefik.http.routers.router1.tls": "true",
 "traefik.http.routers.router1.tls.certresolver": "foobar",
 "traefik.http.routers.router1.tls.domains[0].main": "foobar",
 "traefik.http.routers.router1.tls.domains[0].sans": "foobar, foobar",
 "traefik.http.routers.router1.tls.domains[1].main": "foobar",
 "traefik.http.routers.router1.tls.domains[1].sans": "foobar, foobar",
 "traefik.http.routers.router1.tls.options": "foobar",
-"traefik.http.services.service0.loadbalancer.healthcheck.headers.name0": "foobar",
-"traefik.http.services.service0.loadbalancer.healthcheck.headers.name1": "foobar",
-"traefik.http.services.service0.loadbalancer.healthcheck.hostname": "foobar",
-"traefik.http.services.service0.loadbalancer.healthcheck.interval": "foobar",
-"traefik.http.services.service0.loadbalancer.healthcheck.path": "foobar",
-"traefik.http.services.service0.loadbalancer.healthcheck.port": "42",
-"traefik.http.services.service0.loadbalancer.healthcheck.scheme": "foobar",
-"traefik.http.services.service0.loadbalancer.healthcheck.timeout": "foobar",
-"traefik.http.services.service0.loadbalancer.passhostheader": "true",
-"traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval": "foobar",
-"traefik.http.services.service0.loadbalancer.sticky": "true",
-"traefik.http.services.service0.loadbalancer.sticky.cookie.httponly": "true",
-"traefik.http.services.service0.loadbalancer.sticky.cookie.name": "foobar",
-"traefik.http.services.service0.loadbalancer.sticky.cookie.secure": "true",
-"traefik.http.services.service0.loadbalancer.server.port": "foobar",
-"traefik.http.services.service0.loadbalancer.server.scheme": "foobar",
-"traefik.http.services.service1.loadbalancer.healthcheck.headers.name0": "foobar",
-"traefik.http.services.service1.loadbalancer.healthcheck.headers.name1": "foobar",
-"traefik.http.services.service1.loadbalancer.healthcheck.hostname": "foobar",
-"traefik.http.services.service1.loadbalancer.healthcheck.interval": "foobar",
-"traefik.http.services.service1.loadbalancer.healthcheck.path": "foobar",
-"traefik.http.services.service1.loadbalancer.healthcheck.port": "42",
-"traefik.http.services.service1.loadbalancer.healthcheck.scheme": "foobar",
-"traefik.http.services.service1.loadbalancer.healthcheck.timeout": "foobar",
-"traefik.http.services.service1.loadbalancer.passhostheader": "true",
-"traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval": "foobar",
-"traefik.http.services.service1.loadbalancer.sticky": "true",
-"traefik.http.services.service1.loadbalancer.sticky.cookie.httponly": "true",
-"traefik.http.services.service1.loadbalancer.sticky.cookie.name": "foobar",
-"traefik.http.services.service1.loadbalancer.sticky.cookie.secure": "true",
-"traefik.http.services.service1.loadbalancer.server.port": "foobar",
-"traefik.http.services.service1.loadbalancer.server.scheme": "foobar",
+"traefik.http.services.service01.loadbalancer.healthcheck.headers.name0": "foobar",
+"traefik.http.services.service01.loadbalancer.healthcheck.headers.name1": "foobar",
+"traefik.http.services.service01.loadbalancer.healthcheck.hostname": "foobar",
+"traefik.http.services.service01.loadbalancer.healthcheck.interval": "foobar",
+"traefik.http.services.service01.loadbalancer.healthcheck.path": "foobar",
+"traefik.http.services.service01.loadbalancer.healthcheck.port": "42",
+"traefik.http.services.service01.loadbalancer.healthcheck.scheme": "foobar",
+"traefik.http.services.service01.loadbalancer.healthcheck.timeout": "foobar",
+"traefik.http.services.service01.loadbalancer.passhostheader": "true",
+"traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval": "foobar",
+"traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true",
+"traefik.http.services.service01.loadbalancer.sticky.cookie.name": "foobar",
+"traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true",
+"traefik.http.services.service01.loadbalancer.server.port": "foobar",
+"traefik.http.services.service01.loadbalancer.server.scheme": "foobar",
 "traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar",
 "traefik.tcp.routers.tcprouter0.rule": "foobar",
 "traefik.tcp.routers.tcprouter0.service": "foobar",
-"traefik.tcp.routers.tcprouter0.tls": "true",
 "traefik.tcp.routers.tcprouter0.tls.certresolver": "foobar",
 "traefik.tcp.routers.tcprouter0.tls.domains[0].main": "foobar",
 "traefik.tcp.routers.tcprouter0.tls.domains[0].sans": "foobar, foobar",
@@ -175,7 +156,6 @@
 "traefik.tcp.routers.tcprouter1.entrypoints": "foobar, foobar",
 "traefik.tcp.routers.tcprouter1.rule": "foobar",
 "traefik.tcp.routers.tcprouter1.service": "foobar",
-"traefik.tcp.routers.tcprouter1.tls": "true",
 "traefik.tcp.routers.tcprouter1.tls.certresolver": "foobar",
 "traefik.tcp.routers.tcprouter1.tls.domains[0].main": "foobar",
 "traefik.tcp.routers.tcprouter1.tls.domains[0].sans": "foobar, foobar",
@@ -183,7 +163,5 @@
 "traefik.tcp.routers.tcprouter1.tls.domains[1].sans": "foobar, foobar",
 "traefik.tcp.routers.tcprouter1.tls.options": "foobar",
 "traefik.tcp.routers.tcprouter1.tls.passthrough": "true",
-"traefik.tcp.services.tcpservice0.loadbalancer.server.port": "foobar",
-"traefik.tcp.services.tcpservice0.loadbalancer.terminationDelay": "100",
-"traefik.tcp.services.tcpservice1.loadbalancer.server.port": "foobar"
-"traefik.tcp.services.tcpservice1.loadbalancer.terminationDelay": "100",
+"traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay": "42",
+"traefik.tcp.services.tcpservice01.loadbalancer.server.port": "foobar",
diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md
index 2b98b39d6..fa4eafde4 100644
--- a/docs/content/routing/providers/kubernetes-crd.md
+++ b/docs/content/routing/providers/kubernetes-crd.md
@@ -267,7 +267,7 @@ metadata:
 
 spec:
   entryPoints:
-    - web
+    - websecure
   routes:
   - match: Host(`foo.com`) && PathPrefix(`/bar`)
     kind: Rule
diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md
index 4e00a72cc..773b71d16 100644
--- a/docs/content/routing/routers/index.md
+++ b/docs/content/routing/routers/index.md
@@ -300,7 +300,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
     
     The previous table shows that `Router-1` has a higher priority than `Router-2`.
     
-    To solve this issue, the priority must be setted.
+    To solve this issue, the priority must be set.
 
 ??? example "Set priorities -- using the [File Provider](../../providers/file.md)"
     
@@ -419,10 +419,6 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted
           tls: {}
     ```
 
-!!! info "HTTPS & ACME"
-
-    In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
-
 !!! important "Routers for HTTP & HTTPS"
 
     If you need to define the same route for both HTTP and HTTPS requests, you will need to define two different routers:
@@ -846,10 +842,6 @@ Services are the target for the router.
             passthrough: true
     ```
 
-!!! info "TLS & ACME"
-
-    In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
-
 #### `options`
 
 The `options` field enables fine-grained control of the TLS parameters.  
diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md
index 0b582918d..f3e03ebda 100644
--- a/docs/content/routing/services/index.md
+++ b/docs/content/routing/services/index.md
@@ -599,12 +599,12 @@ This strategy can only be defined with [File](../../providers/file.md).
   [tcp.services.appv1]
     [tcp.services.appv1.loadBalancer]
       [[tcp.services.appv1.loadBalancer.servers]]
-        address = "private-ip-server-1/:8080"
+        address = "private-ip-server-1:8080/"
 
   [tcp.services.appv2]
     [tcp.services.appv2.loadBalancer]
       [[tcp.services.appv2.loadBalancer.servers]]
-        address = "private-ip-server-2/:8080"
+        address = "private-ip-server-2:8080/"
 ```
 
 ```yaml tab="YAML"
diff --git a/docs/docs.Dockerfile b/docs/docs.Dockerfile
index 7c06736e7..024053ee9 100644
--- a/docs/docs.Dockerfile
+++ b/docs/docs.Dockerfile
@@ -1,4 +1,4 @@
-FROM alpine:3.9
+FROM alpine:3.10
 
 ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin
 
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index bda577abe..50cd3f807 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -42,6 +42,9 @@ extra_javascript:
 
 plugins:
   - search
+  - exclude:
+      glob:
+        - include-*.md
 
 # https://squidfunk.github.io/mkdocs-material/extensions/admonition/
 # https://facelessuser.github.io/pymdown-extensions/
@@ -156,6 +159,7 @@ nav:
       - 'Thank You!': 'contributing/thank-you.md'
       - 'Submitting Issues': 'contributing/submitting-issues.md'
       - 'Submitting PRs': 'contributing/submitting-pull-requests.md'
+      - 'Security': 'contributing/submitting-security-issues.md'
       - 'Building and Testing': 'contributing/building-testing.md'
       - 'Documentation': 'contributing/documentation.md'
       - 'Data Collection': 'contributing/data-collection.md'
diff --git a/docs/requirements.txt b/docs/requirements.txt
index e6552aa6a..28465b6c2 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,6 @@
 mkdocs==1.0.4
-pymdown-extensions==6.0
+pymdown-extensions==6.1
 mkdocs-bootswatch==1.0
-mkdocs-material==4.0.2
+mkdocs-material==4.4.3
 markdown-include==0.5.1
+mkdocs-exclude==1.0.2
diff --git a/exp.Dockerfile b/exp.Dockerfile
index d13f64a7a..730db9a40 100644
--- a/exp.Dockerfile
+++ b/exp.Dockerfile
@@ -7,8 +7,8 @@ RUN mkdir -p $WEBUI_DIR
 COPY ./webui/ $WEBUI_DIR/
 
 WORKDIR $WEBUI_DIR
-RUN npm install
 
+RUN npm install
 RUN npm run build
 
 # BUILD
@@ -38,10 +38,12 @@ COPY --from=webui /src/static/ /go/src/github.com/containous/traefik/static/
 RUN ./script/make.sh generate binary
 
 ## IMAGE
-FROM scratch
+FROM alpine:3.10
+
+RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata \
+    && update-ca-certificates \
+    && rm -rf /var/cache/apk/*
 
-COPY --from=gobuild /usr/share/zoneinfo /usr/share/zoneinfo
-COPY --from=gobuild /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
 COPY --from=gobuild /go/src/github.com/containous/traefik/dist/traefik /
 
 EXPOSE 80
diff --git a/go.mod b/go.mod
index 08214219d..247b44493 100644
--- a/go.mod
+++ b/go.mod
@@ -39,7 +39,7 @@ require (
 	github.com/felixge/httpsnoop v1.0.0 // indirect
 	github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
 	github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
-	github.com/go-acme/lego/v3 v3.1.0
+	github.com/go-acme/lego/v3 v3.2.0
 	github.com/go-check/check v0.0.0-00010101000000-000000000000
 	github.com/go-kit/kit v0.9.0
 	github.com/golang/protobuf v1.3.2
@@ -67,7 +67,7 @@ require (
 	github.com/opencontainers/runc v1.0.0-rc8 // indirect
 	github.com/opentracing/basictracer-go v1.0.0 // indirect
 	github.com/opentracing/opentracing-go v1.1.0
-	github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.3
+	github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4
 	github.com/openzipkin/zipkin-go v0.2.1
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/philhofer/fwd v1.0.0 // indirect
@@ -83,7 +83,7 @@ require (
 	github.com/uber/jaeger-client-go v2.19.0+incompatible
 	github.com/uber/jaeger-lib v2.2.0+incompatible
 	github.com/unrolled/render v1.0.1
-	github.com/unrolled/secure v1.0.4
+	github.com/unrolled/secure v1.0.5
 	github.com/vdemeester/shakers v0.1.0
 	github.com/vulcand/oxy v1.0.0
 	github.com/vulcand/predicate v1.1.0
diff --git a/go.sum b/go.sum
index dad56baaf..41e147fac 100644
--- a/go.sum
+++ b/go.sum
@@ -193,8 +193,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 h1:df6OFl8WNXk82xxP3R9ZPZ5seOA8XZkwLdbEzZF1/xI=
 github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLyXJD41gBO/NPKVPGQbhyyC06eugGy15QEZyUkE2/s=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-acme/lego/v3 v3.1.0 h1:yanYFoYW8azFkCvJfIk7edWWfjkYkhDxe45ZsxoW4Xk=
-github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
+github.com/go-acme/lego/v3 v3.2.0 h1:z0zvNlL1niv/1qA06V5X1BRC5PeLoGKAlVaWthXQz9c=
+github.com/go-acme/lego/v3 v3.2.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
 github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
 github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@@ -454,8 +454,8 @@ github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7l
 github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
 github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.3 h1:XudIMByQMXJ6oDHy4SipNyo35LxjA69Z7v1nL0aAZvA=
-github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.3/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4 h1:bzTJRoOZEN7uI1gq594S5HhMYNSud4FKUEwd4aFbsEI=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
 github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
@@ -560,8 +560,8 @@ github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/
 github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 github.com/unrolled/render v1.0.1 h1:VDDnQQVfBMsOsp3VaCJszSO0nkBIVEYoPWeRThk9spY=
 github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM=
-github.com/unrolled/secure v1.0.4 h1:DksfKsRTyXP2R8quDdOOuRpRO45VprFL0X9t9+JX1PU=
-github.com/unrolled/secure v1.0.4/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc=
+github.com/unrolled/secure v1.0.5 h1:KRGJ8DQC3jKpERjBKF3H6b3HcAsM/SRTVwfNJnWs25E=
+github.com/unrolled/secure v1.0.5/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/vdemeester/shakers v0.1.0 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM=
 github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ=
diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go
index 435f1c52d..b729ca563 100644
--- a/pkg/config/dynamic/middlewares.go
+++ b/pkg/config/dynamic/middlewares.go
@@ -357,7 +357,13 @@ type Retry struct {
 
 // StripPrefix holds the StripPrefix configuration.
 type StripPrefix struct {
-	Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty"`
+	Prefixes   []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty"`
+	ForceSlash bool     `json:"forceSlash,omitempty" toml:"forceSlash,omitempty" yaml:"forceSlash,omitempty"` // Deprecated
+}
+
+// SetDefaults Default values for a StripPrefix.
+func (s *StripPrefix) SetDefaults() {
+	s.ForceSlash = true
 }
 
 // +k8s:deepcopy-gen=true
diff --git a/pkg/middlewares/addprefix/add_prefix.go b/pkg/middlewares/addprefix/add_prefix.go
index 655e874a8..934b7e0ae 100644
--- a/pkg/middlewares/addprefix/add_prefix.go
+++ b/pkg/middlewares/addprefix/add_prefix.go
@@ -41,23 +41,35 @@ func New(ctx context.Context, next http.Handler, config dynamic.AddPrefix, name
 	return result, nil
 }
 
-func (ap *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) {
-	return ap.name, tracing.SpanKindNoneEnum
+func (a *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) {
+	return a.name, tracing.SpanKindNoneEnum
 }
 
-func (ap *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), ap.name, typeName))
+func (a *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+	logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), a.name, typeName))
 
 	oldURLPath := req.URL.Path
-	req.URL.Path = ap.prefix + req.URL.Path
+	req.URL.Path = ensureLeadingSlash(a.prefix + req.URL.Path)
 	logger.Debugf("URL.Path is now %s (was %s).", req.URL.Path, oldURLPath)
 
 	if req.URL.RawPath != "" {
 		oldURLRawPath := req.URL.RawPath
-		req.URL.RawPath = ap.prefix + req.URL.RawPath
+		req.URL.RawPath = ensureLeadingSlash(a.prefix + req.URL.RawPath)
 		logger.Debugf("URL.RawPath is now %s (was %s).", req.URL.RawPath, oldURLRawPath)
 	}
 	req.RequestURI = req.URL.RequestURI()
 
-	ap.next.ServeHTTP(rw, req)
+	a.next.ServeHTTP(rw, req)
+}
+
+func ensureLeadingSlash(str string) string {
+	if str == "" {
+		return str
+	}
+
+	if str[0] == '/' {
+		return str
+	}
+
+	return "/" + str
 }
diff --git a/pkg/middlewares/ratelimiter/rate_limiter.go b/pkg/middlewares/ratelimiter/rate_limiter.go
index 4e51ec3c9..2940c22ae 100644
--- a/pkg/middlewares/ratelimiter/rate_limiter.go
+++ b/pkg/middlewares/ratelimiter/rate_limiter.go
@@ -5,7 +5,6 @@ import (
 	"context"
 	"fmt"
 	"net/http"
-	"sync"
 	"time"
 
 	"github.com/containous/traefik/v2/pkg/config/dynamic"
@@ -35,8 +34,7 @@ type rateLimiter struct {
 	sourceMatcher utils.SourceExtractor
 	next          http.Handler
 
-	bucketsMu sync.Mutex
-	buckets   *ttlmap.TtlMap // actual buckets, keyed by source.
+	buckets *ttlmap.TtlMap // actual buckets, keyed by source.
 }
 
 // New returns a rate limiter middleware.
@@ -57,7 +55,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name
 		return nil, err
 	}
 
-	buckets, err := ttlmap.NewMap(maxSources)
+	buckets, err := ttlmap.NewConcurrent(maxSources)
 	if err != nil {
 		return nil, err
 	}
@@ -104,9 +102,6 @@ func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		logger.Infof("ignoring token bucket amount > 1: %d", amount)
 	}
 
-	rl.bucketsMu.Lock()
-	defer rl.bucketsMu.Unlock()
-
 	var bucket *rate.Limiter
 	if rlSource, exists := rl.buckets.Get(source); exists {
 		bucket = rlSource.(*rate.Limiter)
diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go
index 0e87013c3..9224a3252 100644
--- a/pkg/middlewares/redirect/redirect.go
+++ b/pkg/middlewares/redirect/redirect.go
@@ -132,19 +132,13 @@ func rawURL(req *http.Request) string {
 		uri = match[4]
 	}
 
-	if req.TLS != nil || isXForwardedHTTPS(req) {
+	if req.TLS != nil {
 		scheme = "https"
 	}
 
 	return strings.Join([]string{scheme, "://", host, port, uri}, "")
 }
 
-func isXForwardedHTTPS(request *http.Request) bool {
-	xForwardedProto := request.Header.Get("X-Forwarded-Proto")
-
-	return len(xForwardedProto) > 0 && xForwardedProto == "https"
-}
-
 func applyString(in string, out io.Writer, req *http.Request) error {
 	t, err := template.New("t").Parse(in)
 	if err != nil {
diff --git a/pkg/middlewares/stripprefix/strip_prefix.go b/pkg/middlewares/stripprefix/strip_prefix.go
index b8008689a..1a0fd8bbc 100644
--- a/pkg/middlewares/stripprefix/strip_prefix.go
+++ b/pkg/middlewares/stripprefix/strip_prefix.go
@@ -20,18 +20,20 @@ const (
 
 // stripPrefix is a middleware used to strip prefix from an URL request.
 type stripPrefix struct {
-	next     http.Handler
-	prefixes []string
-	name     string
+	next       http.Handler
+	prefixes   []string
+	forceSlash bool // TODO Must be removed (breaking), the default behavior must be forceSlash=false
+	name       string
 }
 
 // New creates a new strip prefix middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.StripPrefix, name string) (http.Handler, error) {
 	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
 	return &stripPrefix{
-		prefixes: config.Prefixes,
-		next:     next,
-		name:     name,
+		prefixes:   config.Prefixes,
+		forceSlash: config.ForceSlash,
+		next:       next,
+		name:       name,
 	}, nil
 }
 
@@ -42,9 +44,9 @@ func (s *stripPrefix) GetTracingInformation() (string, ext.SpanKindEnum) {
 func (s *stripPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	for _, prefix := range s.prefixes {
 		if strings.HasPrefix(req.URL.Path, prefix) {
-			req.URL.Path = getPrefixStripped(req.URL.Path, prefix)
+			req.URL.Path = s.getPrefixStripped(req.URL.Path, prefix)
 			if req.URL.RawPath != "" {
-				req.URL.RawPath = getPrefixStripped(req.URL.RawPath, prefix)
+				req.URL.RawPath = s.getPrefixStripped(req.URL.RawPath, prefix)
 			}
 			s.serveRequest(rw, req, strings.TrimSpace(prefix))
 			return
@@ -59,10 +61,25 @@ func (s *stripPrefix) serveRequest(rw http.ResponseWriter, req *http.Request, pr
 	s.next.ServeHTTP(rw, req)
 }
 
-func getPrefixStripped(s, prefix string) string {
-	return ensureLeadingSlash(strings.TrimPrefix(s, prefix))
+func (s *stripPrefix) getPrefixStripped(urlPath, prefix string) string {
+	if s.forceSlash {
+		// Only for compatibility reason with the previous behavior,
+		// but the previous behavior is wrong.
+		// This needs to be removed in the next breaking version.
+		return "/" + strings.TrimPrefix(strings.TrimPrefix(urlPath, prefix), "/")
+	}
+
+	return ensureLeadingSlash(strings.TrimPrefix(urlPath, prefix))
 }
 
 func ensureLeadingSlash(str string) string {
-	return "/" + strings.TrimPrefix(str, "/")
+	if str == "" {
+		return str
+	}
+
+	if str[0] == '/' {
+		return str
+	}
+
+	return "/" + str
 }
diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go
index 4024ac4f0..4feda225e 100644
--- a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go
+++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go
@@ -60,12 +60,12 @@ func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request)
 
 			req.Header.Add(stripprefix.ForwardedPrefixHeader, prefix)
 
-			req.URL.Path = strings.Replace(req.URL.Path, prefix, "", 1)
+			req.URL.Path = ensureLeadingSlash(strings.Replace(req.URL.Path, prefix, "", 1))
 			if req.URL.RawPath != "" {
-				req.URL.RawPath = req.URL.RawPath[len(prefix):]
+				req.URL.RawPath = ensureLeadingSlash(req.URL.RawPath[len(prefix):])
 			}
 
-			req.RequestURI = ensureLeadingSlash(req.URL.RequestURI())
+			req.RequestURI = req.URL.RequestURI()
 			s.next.ServeHTTP(rw, req)
 			return
 		}
@@ -75,5 +75,13 @@ func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request)
 }
 
 func ensureLeadingSlash(str string) string {
-	return "/" + strings.TrimPrefix(str, "/")
+	if str == "" {
+		return str
+	}
+
+	if str[0] == '/' {
+		return str
+	}
+
+	return "/" + str
 }
diff --git a/traefik.sample.toml b/traefik.sample.toml
index bdc001eaf..1f664ac93 100644
--- a/traefik.sample.toml
+++ b/traefik.sample.toml
@@ -95,12 +95,12 @@
 # Enable API and dashboard
 [api]
 
-  # Name of the related entry point
+  # Enable the API in insecure mode
   #
   # Optional
-  # Default: "traefik"
+  # Default: true
   #
-  # entryPoint = "traefik"
+  # insecure = false
 
   # Enabled Dashboard
   #


Test patch:
diff --git a/docs/content/contributing/building-testing.md b/docs/content/contributing/building-testing.md
index 65240f216..d55e5971d 100644
--- a/docs/content/contributing/building-testing.md
+++ b/docs/content/contributing/building-testing.md
@@ -62,6 +62,7 @@ Requirements:
 
 - `go` v1.13+
 - environment variable `GO111MODULE=on`
+- go-bindata `GO111MODULE=off go get -u github.com/containous/go-bindata/...`
 
 !!! tip "Source Directory"
 
diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go
index 63ac2f48f..fbfece11e 100644
--- a/pkg/config/label/label_test.go
+++ b/pkg/config/label/label_test.go
@@ -368,6 +368,7 @@ func TestDecodeConfiguration(t *testing.T) {
 							"foobar",
 							"fiibar",
 						},
+						ForceSlash: true,
 					},
 				},
 				"Middleware18": {
@@ -771,6 +772,7 @@ func TestEncodeConfiguration(t *testing.T) {
 							"foobar",
 							"fiibar",
 						},
+						ForceSlash: true,
 					},
 				},
 				"Middleware18": {
@@ -1091,6 +1093,7 @@ func TestEncodeConfiguration(t *testing.T) {
 		"traefik.HTTP.Middlewares.Middleware15.ReplacePathRegex.Replacement":                       "foobar",
 		"traefik.HTTP.Middlewares.Middleware16.Retry.Attempts":                                     "42",
 		"traefik.HTTP.Middlewares.Middleware17.StripPrefix.Prefixes":                               "foobar, fiibar",
+		"traefik.HTTP.Middlewares.Middleware17.StripPrefix.ForceSlash":                             "true",
 		"traefik.HTTP.Middlewares.Middleware18.StripPrefixRegex.Regex":                             "foobar, fiibar",
 		"traefik.HTTP.Middlewares.Middleware19.Compress":                                           "true",
 
diff --git a/pkg/metrics/prometheus_test.go b/pkg/metrics/prometheus_test.go
index de66a421c..03df22465 100644
--- a/pkg/metrics/prometheus_test.go
+++ b/pkg/metrics/prometheus_test.go
@@ -290,6 +290,11 @@ func TestPrometheusMetricRemoval(t *testing.T) {
 			th.WithLoadBalancerServices(th.WithService("bar@providerName",
 				th.WithServers(th.WithServer("http://localhost:9000"))),
 			),
+			func(cfg *dynamic.HTTPConfiguration) {
+				cfg.Services["fii"] = &dynamic.Service{
+					Weighted: &dynamic.WeightedRoundRobin{},
+				}
+			},
 		),
 	}
 
diff --git a/pkg/middlewares/addprefix/add_prefix_test.go b/pkg/middlewares/addprefix/add_prefix_test.go
index 1d130a083..cca4bd19e 100644
--- a/pkg/middlewares/addprefix/add_prefix_test.go
+++ b/pkg/middlewares/addprefix/add_prefix_test.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/containous/traefik/v2/pkg/config/dynamic"
 	"github.com/containous/traefik/v2/pkg/testhelpers"
-	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -47,7 +46,6 @@ func TestNewAddPrefix(t *testing.T) {
 }
 
 func TestAddPrefix(t *testing.T) {
-	logrus.SetLevel(logrus.DebugLevel)
 	testCases := []struct {
 		desc            string
 		prefix          dynamic.AddPrefix
@@ -61,6 +59,12 @@ func TestAddPrefix(t *testing.T) {
 			path:         "/b",
 			expectedPath: "/a/b",
 		},
+		{
+			desc:         "Works with missing leading slash",
+			prefix:       dynamic.AddPrefix{Prefix: "a"},
+			path:         "/",
+			expectedPath: "/a/",
+		},
 		{
 			desc:            "Works with a raw path",
 			prefix:          dynamic.AddPrefix{Prefix: "/a"},
diff --git a/pkg/middlewares/redirect/redirect_regex_test.go b/pkg/middlewares/redirect/redirect_regex_test.go
index ca4edbb79..fd4d71640 100644
--- a/pkg/middlewares/redirect/redirect_regex_test.go
+++ b/pkg/middlewares/redirect/redirect_regex_test.go
@@ -19,6 +19,7 @@ func TestRedirectRegexHandler(t *testing.T) {
 		config         dynamic.RedirectRegex
 		method         string
 		url            string
+		headers        map[string]string
 		secured        bool
 		expectedURL    string
 		expectedStatus int
@@ -104,6 +105,19 @@ func TestRedirectRegexHandler(t *testing.T) {
 			expectedURL:    "https://foo:443",
 			expectedStatus: http.StatusFound,
 		},
+		{
+			desc: "HTTP to HTTPS, with X-Forwarded-Proto",
+			config: dynamic.RedirectRegex{
+				Regex:       `http://foo:80`,
+				Replacement: "https://foo:443",
+			},
+			url: "http://foo:80",
+			headers: map[string]string{
+				"X-Forwarded-Proto": "https",
+			},
+			expectedURL:    "https://foo:443",
+			expectedStatus: http.StatusFound,
+		},
 		{
 			desc: "HTTPS to HTTP",
 			config: dynamic.RedirectRegex{
@@ -171,12 +185,18 @@ func TestRedirectRegexHandler(t *testing.T) {
 				if test.method != "" {
 					method = test.method
 				}
-				r := testhelpers.MustNewRequest(method, test.url, nil)
+
+				req := testhelpers.MustNewRequest(method, test.url, nil)
 				if test.secured {
-					r.TLS = &tls.ConnectionState{}
+					req.TLS = &tls.ConnectionState{}
 				}
-				r.Header.Set("X-Foo", "bar")
-				handler.ServeHTTP(recorder, r)
+
+				for k, v := range test.headers {
+					req.Header.Set(k, v)
+				}
+
+				req.Header.Set("X-Foo", "bar")
+				handler.ServeHTTP(recorder, req)
 
 				assert.Equal(t, test.expectedStatus, recorder.Code)
 				switch test.expectedStatus {
diff --git a/pkg/middlewares/redirect/redirect_scheme_test.go b/pkg/middlewares/redirect/redirect_scheme_test.go
index 384780d53..b0694cd57 100644
--- a/pkg/middlewares/redirect/redirect_scheme_test.go
+++ b/pkg/middlewares/redirect/redirect_scheme_test.go
@@ -19,6 +19,7 @@ func TestRedirectSchemeHandler(t *testing.T) {
 		config         dynamic.RedirectScheme
 		method         string
 		url            string
+		headers        map[string]string
 		secured        bool
 		expectedURL    string
 		expectedStatus int
@@ -39,6 +40,18 @@ func TestRedirectSchemeHandler(t *testing.T) {
 			expectedURL:    "https://foo",
 			expectedStatus: http.StatusFound,
 		},
+		{
+			desc: "HTTP to HTTPS, with X-Forwarded-Proto",
+			config: dynamic.RedirectScheme{
+				Scheme: "https",
+			},
+			url: "http://foo",
+			headers: map[string]string{
+				"X-Forwarded-Proto": "https",
+			},
+			expectedURL:    "https://foo",
+			expectedStatus: http.StatusFound,
+		},
 		{
 			desc: "HTTP with port to HTTPS without port",
 			config: dynamic.RedirectScheme{
@@ -197,13 +210,17 @@ func TestRedirectSchemeHandler(t *testing.T) {
 				if test.method != "" {
 					method = test.method
 				}
-				r := httptest.NewRequest(method, test.url, nil)
+				req := httptest.NewRequest(method, test.url, nil)
+
+				for k, v := range test.headers {
+					req.Header.Set(k, v)
+				}
 
 				if test.secured {
-					r.TLS = &tls.ConnectionState{}
+					req.TLS = &tls.ConnectionState{}
 				}
-				r.Header.Set("X-Foo", "bar")
-				handler.ServeHTTP(recorder, r)
+				req.Header.Set("X-Foo", "bar")
+				handler.ServeHTTP(recorder, req)
 
 				assert.Equal(t, test.expectedStatus, recorder.Code)
 
@@ -223,9 +240,9 @@ func TestRedirectSchemeHandler(t *testing.T) {
 
 				if re.Match([]byte(test.url)) {
 					match := re.FindStringSubmatch(test.url)
-					r.RequestURI = match[4]
+					req.RequestURI = match[4]
 
-					handler.ServeHTTP(recorder, r)
+					handler.ServeHTTP(recorder, req)
 
 					assert.Equal(t, test.expectedStatus, recorder.Code)
 					if test.expectedStatus == http.StatusMovedPermanently ||
diff --git a/pkg/middlewares/stripprefix/strip_prefix_test.go b/pkg/middlewares/stripprefix/strip_prefix_test.go
index 449720902..13a5aedb2 100644
--- a/pkg/middlewares/stripprefix/strip_prefix_test.go
+++ b/pkg/middlewares/stripprefix/strip_prefix_test.go
@@ -31,6 +31,17 @@ func TestStripPrefix(t *testing.T) {
 			expectedStatusCode: http.StatusOK,
 			expectedPath:       "/noprefixes",
 		},
+		{
+			desc: "wildcard (.*) requests (ForceSlash)",
+			config: dynamic.StripPrefix{
+				Prefixes:   []string{"/"},
+				ForceSlash: true,
+			},
+			path:               "/",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "/",
+			expectedHeader:     "/",
+		},
 		{
 			desc: "wildcard (.*) requests",
 			config: dynamic.StripPrefix{
@@ -38,9 +49,20 @@ func TestStripPrefix(t *testing.T) {
 			},
 			path:               "/",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "/",
+			expectedPath:       "",
 			expectedHeader:     "/",
 		},
+		{
+			desc: "prefix and path matching (ForceSlash)",
+			config: dynamic.StripPrefix{
+				Prefixes:   []string{"/stat"},
+				ForceSlash: true,
+			},
+			path:               "/stat",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "/",
+			expectedHeader:     "/stat",
+		},
 		{
 			desc: "prefix and path matching",
 			config: dynamic.StripPrefix{
@@ -48,9 +70,20 @@ func TestStripPrefix(t *testing.T) {
 			},
 			path:               "/stat",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "/",
+			expectedPath:       "",
 			expectedHeader:     "/stat",
 		},
+		{
+			desc: "path prefix on exactly matching path (ForceSlash)",
+			config: dynamic.StripPrefix{
+				Prefixes:   []string{"/stat/"},
+				ForceSlash: true,
+			},
+			path:               "/stat/",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "/",
+			expectedHeader:     "/stat/",
+		},
 		{
 			desc: "path prefix on exactly matching path",
 			config: dynamic.StripPrefix{
@@ -58,7 +91,7 @@ func TestStripPrefix(t *testing.T) {
 			},
 			path:               "/stat/",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "/",
+			expectedPath:       "",
 			expectedHeader:     "/stat/",
 		},
 		{
@@ -101,6 +134,17 @@ func TestStripPrefix(t *testing.T) {
 			expectedPath:       "/us",
 			expectedHeader:     "/stat",
 		},
+		{
+			desc: "later prefix matching (ForceSlash)",
+			config: dynamic.StripPrefix{
+				Prefixes:   []string{"/mismatch", "/stat"},
+				ForceSlash: true,
+			},
+			path:               "/stat",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "/",
+			expectedHeader:     "/stat",
+		},
 		{
 			desc: "later prefix matching",
 			config: dynamic.StripPrefix{
@@ -108,7 +152,7 @@ func TestStripPrefix(t *testing.T) {
 			},
 			path:               "/stat",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "/",
+			expectedPath:       "",
 			expectedHeader:     "/stat",
 		},
 		{
@@ -162,12 +206,15 @@ func TestStripPrefix(t *testing.T) {
 			assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
 			assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader)
 
-			expectedURI := test.expectedPath
+			expectedRequestURI := test.expectedPath
 			if test.expectedRawPath != "" {
 				// go HTTP uses the raw path when existent in the RequestURI
-				expectedURI = test.expectedRawPath
+				expectedRequestURI = test.expectedRawPath
+			}
+			if test.expectedPath == "" {
+				expectedRequestURI = "/"
 			}
-			assert.Equal(t, expectedURI, requestURI, "Unexpected request URI.")
+			assert.Equal(t, expectedRequestURI, requestURI, "Unexpected request URI.")
 		})
 	}
 }
diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go
index 297fa7192..6e0293083 100644
--- a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go
+++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go
@@ -31,42 +31,66 @@ func TestStripPrefixRegex(t *testing.T) {
 			expectedPath:       "/a/test",
 		},
 		{
-			path:               "/a/test",
+			path:               "/a/test/",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "/a/test",
+			expectedPath:       "/a/test/",
+		},
+		{
+			path:               "/a/api/",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "",
+			expectedHeader:     "/a/api/",
 		},
 		{
 			path:               "/a/api/test",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "test",
+			expectedPath:       "/test",
+			expectedHeader:     "/a/api/",
+		},
+		{
+			path:               "/a/api/test/",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "/test/",
 			expectedHeader:     "/a/api/",
 		},
 		{
 			path:               "/b/api/",
 			expectedStatusCode: http.StatusOK,
+			expectedPath:       "",
 			expectedHeader:     "/b/api/",
 		},
+		{
+			path:               "/b/api",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "/b/api",
+		},
 		{
 			path:               "/b/api/test1",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "test1",
+			expectedPath:       "/test1",
 			expectedHeader:     "/b/api/",
 		},
 		{
 			path:               "/b/api2/test2",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "test2",
+			expectedPath:       "/test2",
 			expectedHeader:     "/b/api2/",
 		},
 		{
 			path:               "/c/api/123/",
 			expectedStatusCode: http.StatusOK,
+			expectedPath:       "",
 			expectedHeader:     "/c/api/123/",
 		},
+		{
+			path:               "/c/api/123",
+			expectedStatusCode: http.StatusOK,
+			expectedPath:       "/c/api/123",
+		},
 		{
 			path:               "/c/api/123/test3",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "test3",
+			expectedPath:       "/test3",
 			expectedHeader:     "/c/api/123/",
 		},
 		{
@@ -77,8 +101,8 @@ func TestStripPrefixRegex(t *testing.T) {
 		{
 			path:               "/a/api/a%2Fb",
 			expectedStatusCode: http.StatusOK,
-			expectedPath:       "a/b",
-			expectedRawPath:    "a%2Fb",
+			expectedPath:       "/a/b",
+			expectedRawPath:    "/a%2Fb",
 			expectedHeader:     "/a/api/",
 		},
 	}
@@ -88,11 +112,12 @@ func TestStripPrefixRegex(t *testing.T) {
 		t.Run(test.path, func(t *testing.T) {
 			t.Parallel()
 
-			var actualPath, actualRawPath, actualHeader string
+			var actualPath, actualRawPath, actualHeader, requestURI string
 			handlerPath := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				actualPath = r.URL.Path
 				actualRawPath = r.URL.RawPath
 				actualHeader = r.Header.Get(stripprefix.ForwardedPrefixHeader)
+				requestURI = r.RequestURI
 			})
 			handler, err := New(context.Background(), handlerPath, testPrefixRegex, "foo-strip-prefix-regex")
 			require.NoError(t, err)
@@ -106,6 +131,18 @@ func TestStripPrefixRegex(t *testing.T) {
 			assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
 			assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
 			assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", stripprefix.ForwardedPrefixHeader)
+
+			if test.expectedPath != test.path {
+				expectedRequestURI := test.expectedPath
+				if test.expectedRawPath != "" {
+					// go HTTP uses the raw path when existent in the RequestURI
+					expectedRequestURI = test.expectedRawPath
+				}
+				if test.expectedPath == "" {
+					expectedRequestURI = "/"
+				}
+				assert.Equal(t, expectedRequestURI, requestURI, "Unexpected request URI.")
+			}
 		})
 	}
 }