Repository: gofiber/fiber
Pull request #2065 (2022-08-30 14:33:03)

✨ v3 (feature): new Route method
related to #1829 


Patch:
diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
index 3d1f2b57..c8d7ee44 100644
--- a/.github/CODE_OF_CONDUCT.md
+++ b/.github/CODE_OF_CONDUCT.md
@@ -36,7 +36,7 @@ This Code of Conduct applies within all community spaces, and also applies when
 
 ## Enforcement
 
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Gitter](https://gitter.im/gofiber/community). All complaints will be reviewed and investigated promptly and fairly.
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Discord](https://gofiber.io/discord). All complaints will be reviewed and investigated promptly and fairly.
 
 All community leaders are obligated to respect the privacy and security of the reporter of any incident.
 
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index da8566f7..7a84e21e 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,6 +1,6 @@
 # Contributing
 
-When contributing to this repository, please first discuss the change you wish to make via our [Telegram](https://t.me/gofiber) group, by creating an [issue](https://github.com/gofiber/fiber/issues) or any other method with the owners of this repository before making a change.
+When contributing to this repository, please first discuss the change you wish to make via our [Discord](https://gofiber.io/discord) server, by creating an [issue](https://github.com/gofiber/fiber/issues) or any other method with the owners of this repository before making a change.
 
 Please note: we have a [code of conduct](https://github.com/gofiber/fiber/blob/master/.github/CODE_OF_CONDUCT.md), please follow it in all your interactions with the `Fiber` project.
 
@@ -24,4 +24,4 @@ If you want to say **thank you** and/or support the active development of `Fiber
 1. Add a [GitHub Star](https://github.com/gofiber/fiber/stargazers) to the project.
 2. Tweet about the project [on your Twitter](https://twitter.com/intent/tweet?text=%F0%9F%9A%80%20Fiber%20%E2%80%94%20is%20an%20Express.js%20inspired%20web%20framework%20build%20on%20Fasthttp%20for%20%23Go%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber).
 3. Write a review or tutorial on [Medium](https://medium.com/), [Dev.to](https://dev.to/) or personal blog.
-4. Support the project by donating a [cup of coffee](https://buymeacoff.ee/fenny).
\ No newline at end of file
+4. Support the project by donating a [cup of coffee](https://buymeacoff.ee/fenny).
diff --git a/.github/README.md b/.github/README.md
index 181880f7..1b188790 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -148,7 +148,7 @@ Fiber is **inspired** by Express, the most popular web framework on the Internet
 We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues), Discord [channel](https://gofiber.io/discord) _and all over the Internet_ to create a **fast**, **flexible** and **friendly** Go web framework for **any** task, **deadline** and developer **skill**! Just like Express does in the JavaScript world.
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 Examples
@@ -595,13 +595,14 @@ func main() {
 Here is a list of middleware that are included within the Fiber framework.
 
 | Middleware                                                                             | Description                                                                                                                                                                  |
-| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+|:---------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth)         | Basic auth middleware provides an HTTP basic authentication. It calls the next handler for valid credentials and 401 Unauthorized for missing or invalid credentials.        |
 | [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache)                 | Intercept and cache responses                                                                                                                                                |
 | [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress)           | Compression middleware for Fiber, it supports `deflate`, `gzip` and `brotli` by default.                                                                                     |
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_ckb.md b/.github/README_ckb.md
index 07f7d124..3ed941a6 100644
--- a/.github/README_ckb.md
+++ b/.github/README_ckb.md
@@ -601,6 +601,7 @@ func main() {
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_de.md b/.github/README_de.md
index 4fe20dfd..654a0e98 100644
--- a/.github/README_de.md
+++ b/.github/README_de.md
@@ -142,7 +142,7 @@ Neue Gopher, welche von [Node.js](https://nodejs.org/en/about/) zu [Go](https://
 Fiber ist **inspiriert** von Express.js, dem beliebtesten Web-Framework im Internet. Wir haben die **Leichtigkeit** von Express und die **Rohleistung** von Go kombiniert. Wenn du jemals eine Webanwendung mit Node.js implementiert hast (_mit Express.js oder ähnlichem_), werden dir viele Methoden und Prinzipien **sehr vertraut** vorkommen.
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 Beispiele
@@ -571,6 +571,7 @@ Hier finden Sie eine Liste der Middleware, die im Fiber-Framework enthalten ist.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_es.md b/.github/README_es.md
index f690bbb6..1f52ec0b 100644
--- a/.github/README_es.md
+++ b/.github/README_es.md
@@ -79,7 +79,7 @@
   </a>
 </p>
 <p align="center">
-<strong>Fiber</strong> es un <strong>framework web</strong> inspirado en <a href="https://github.com/expressjs/express">Express</a> construido sobre <a href="https://github.com/valyala/fasthttp">Fasthttp</a>, el motor HTTP <strong>más rápido</strong> para <a href="https://go.dev/doc/">Go</a>. Diseñado para <strong>facilitar</strong> las cosas para <strong>un</strong> desarrollo <strong>rápido</strong> con <strong>cero asignación de memoria</strong> y <strong>rendimiento</strong> en mente.
+<strong>Fiber</strong> es un <strong>framework web</strong> inspirado en <a href="https://github.com/expressjs/express">Express</a> construido sobre <a href="https://github.com/valyala/fasthttp">Fasthttp</a>, el motor HTTP <strong>más rápido</strong> para <a href="https://go.dev/doc/">Go</a>. Diseñado para <strong>facilitar</strong> las cosas y tener un <strong>menor tiempo de desarrollo</strong> con <strong>cero asignación de memoria</strong> y pensado para un mejor <strong>rendimiento</strong>.
 </p>
 
 ## ⚡️ Inicio rápido
@@ -142,7 +142,7 @@ Los nuevos gophers que hacen el cambio de [Node.js](https://nodejs.org/en/about/
 Fiber está **inspirado** en Expressjs, el framework web más popular en Internet. Combinamos la **facilidad** de Express y **el rendimiento bruto** de Go. Si alguna vez ha implementado una aplicación web en Node.js ( _utilizando Express.js o similar_ ), muchos métodos y principios le parecerán **muy comunes** .
 
 ## ⚠️ Limitantes
-* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.29.0 ha sido probado con las versiones de Go 1.14 a 1.18.
+* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.29.0 ha sido probado con las versiones de Go 1.14 a 1.19.
 * Fiber no es compatible con interfaces net/http. Esto significa que no lo podrá usar en proyectos como qglgen, go-swagger, u otros que son parte del ecosistema net/http.
 
 ## 👀 Ejemplos
@@ -571,6 +571,7 @@ Aquí está una lista del middleware incluido en el marco web Fiber.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_fa.md b/.github/README_fa.md
index 6ede9a37..930093b1 100644
--- a/.github/README_fa.md
+++ b/.github/README_fa.md
@@ -686,6 +686,7 @@ func main() {
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   |فعال سازی هدر های cross-origin با گزینه های مختلف.|
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   |در برابر حملات CSRF ایمنی ایجاد میکند.|
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) |مقادیر کوکی هارا رمزنگاری میکند.|
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | با ارائه تنظیمات اختیاری، متغیرهای محیط را در معرض دید قرار دهید.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | میدلور ETag به کش ها اجازه میدهد کارآمد تر عمل کرده و در پهنای باند صرفه جویی کنند.  به عنوان یک وب سرور نیازی به دادن پاسخ کامل نیست اگر محتوا تغییر نکرده باشد. |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | میدلور Expvar میتواند متغیر هایی را تعریف کرده و مقادیر انها را در زمان اجرا با فرمت JSON به شما نشان دهد. |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | جلوگیری و یا کش کردن درخواست های favicon در صورتی که مسیر یک فایل را داده باشید.|
diff --git a/.github/README_fr.md b/.github/README_fr.md
index 2fe857f4..084b49dc 100644
--- a/.github/README_fr.md
+++ b/.github/README_fr.md
@@ -142,7 +142,7 @@ Les nouveaux gophers qui passent de [Node.js](https://nodejs.org/en/about/) à [
 Fiber est **inspiré** par Express, le framework web le plus populaire d'Internet. Nous avons combiné la **facilité** d'Express, et la **performance brute** de Go. Si vous avez déja développé une application web en Node.js (_en utilisant Express ou équivalent_), alors de nombreuses méthodes et principes vous sembleront **familiers**.
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 Exemples
@@ -573,6 +573,7 @@ Here is a list of middleware that are included within the Fiber framework.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_he.md b/.github/README_he.md
index f326fe23..3f065fab 100644
--- a/.github/README_he.md
+++ b/.github/README_he.md
@@ -187,7 +187,7 @@ Fiber נוצרה **בהשראת** Express, ה-web framework הפופולרית 
 <div dir="rtl">
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 דוגמאות
@@ -689,6 +689,7 @@ Here is a list of middleware that are included within the Fiber framework.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_id.md b/.github/README_id.md
index 9f544a26..e743c2bb 100644
--- a/.github/README_id.md
+++ b/.github/README_id.md
@@ -145,7 +145,7 @@ Kami **mendengarkan** para pengguna di [GitHub Issues](https://github.com/gofibe
 
 ## ⚠️ Limitasi
 
-* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.29.0 telah diuji dengan Go versi 1.14 hingga 1.18.
+* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.29.0 telah diuji dengan Go versi 1.14 hingga 1.19.
 * Fiber tidak kompatibel dengan antarmuka net/http. Ini berarti kamu tidak akan dapat menggunakan proyek seperti gqlgen, go-swagger, atau lainnya yang merupakan bagian dari ekosistem net/http.
 
 ## 👀 Contoh
@@ -574,6 +574,7 @@ Kumpulan `middleware` yang ada didalam kerangka kerja Fiber.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_it.md b/.github/README_it.md
index 4835d356..4f01cbfd 100644
--- a/.github/README_it.md
+++ b/.github/README_it.md
@@ -82,7 +82,7 @@
 <strong>Fiber</strong> è un <strong>framework web</strong> inspirato a <a href="https://github.com/expressjs/express">Express</a> costruito sopra <a href="https://github.com/valyala/fasthttp">Fasthttp</a>, un motore HTTP <strong>molto veloce</strong> per <a href="https://go.dev/doc/">Go</a>. Progettato per <strong>semplificare</strong> le cose per uno sviluppo <strong>veloce</strong> con <strong>zero allocazione di memoria</strong> e le <strong>prestazioni</strong> in mente.
 </p>
 
-## ⚡️ Avvio rapido
+## ⚡️ Inizia velocemente
 
 ```go
 package main
@@ -111,9 +111,9 @@ Questi test sono stati eseguiti da [TechEmpower](https://www.techempower.com/ben
 
 ## ⚙️ Installazione
 
-Assicurati di avere Go ([download](https://go.dev/dl/)) installato. Versione `1.14` o superiore.
+Assicurati di avere Go ([per scaricalro](https://go.dev/dl/)) installato. Devi avere la versione `1.14` o superiore.
 
-Inizializza il tuo progetto creando una cartella e successivamente lanciando `go mod init github.com/your/repo` ([per maggiori info](https://go.dev/blog/using-go-modules)) da dentro la cartella. Dopo installa Fiber con il comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them):
+Inizializza il tuo progetto creando una cartella e successivamente usando il comando `go mod init github.com/la-tua/repo` ([per maggiori informazioni](https://go.dev/blog/using-go-modules)) dentro la cartella. Dopodiche installa Fiber con il comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them):
 
 ```bash
 go get -u github.com/gofiber/fiber/v3
@@ -139,16 +139,16 @@ go get -u github.com/gofiber/fiber/v3
 
 I nuovi gopher che passano da [Node.js](https://nodejs.org/en/about/) a [Go](https://go.dev/doc/) hanno a che fare con una curva di apprendimento prima di poter iniziare a creare le proprie applicazioni web o microservizi. Fiber, come **web framework** , è stato creato con l'idea di **minimalismo** e seguendo lo '**UNIX way**' , così i nuovi gopher posso entrare rapidamente nel mondo di Go con un caldo e fidato benvenuto.
 
-Fiber è **inspirato** da Express, il web framework più popolare su internet. Abbiamo combiniamo la **facilità** di Express e **le pure prestazioni** di Go. Se hai mai implementato una applicazione web in Node.js (_utilizzando Express o simili_), allora i tanti metodi e principi ti saranno **molto familiari**.
+Fiber è **ispirato** da Express, il web framework più popolare su internet. Abbiamo combiniamo la **facilità** di Express e **le prestazioni** di Go. Se hai mai implementato una applicazione web in Node.js (_utilizzando Express o simili_), allora i tanti metodi e principi ti saranno **molto familiari**.
 
 ## ⚠️ Limitazioni
 
-* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.29.0 è stato testato con la versioni 1.14 alla 1.18 di Go.
+* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.29.0 è stato testato con la versioni 1.14 alla 1.19 di Go.
 * Fiber non è compatibile con le interfacce net/http. Questo significa che non è possibile utilizzare progetti come qglgen, go-swagger, o altri che fanno parte dell'ecosistema net/http.
 
 ## 👀 Esempi
 
-Qui sotto trovi molti dei più comuni esempi. Se vuoi vedere ulteriori esempi, visita il nostro [Recipes repository](https://github.com/gofiber/recipes) o la nostra [documentazione API](https://docs.gofiber.io) .
+Qui sotto trovi molti dei più comuni esempi. Se vuoi vedere ulteriori esempi, visita il nostro [repository delle ricette](https://github.com/gofiber/recipes) o la nostra [documentazione API](https://docs.gofiber.io) .
 
 #### 📖 [**Routing di base**](https://docs.gofiber.io/#basic-routing)
 
@@ -191,7 +191,7 @@ func main() {
 
 ```
 
-#### 📖 [**Route Naming**](https://docs.gofiber.io/api/app#name)
+#### 📖 [**Dare nomi alle Route**](https://docs.gofiber.io/api/app#name)
 
 ```go
 func main() {
@@ -275,17 +275,17 @@ func main() {
 <details>
   <summary>📚 Mostra altri esempi</summary>
 
-### View engine
+### Motori di template
 
-📖 [Config](https://docs.gofiber.io/api/fiber#config)
-📖 [Engines](https://github.com/gofiber/template)
+📖 [Configurazione](https://docs.gofiber.io/api/fiber#config)
+📖 [Motori](https://github.com/gofiber/template)
 📖 [Render](https://docs.gofiber.io/api/ctx#render)
 
-Fiber usa di default [html/template](https://pkg.go.dev/html/template/) quando nessun view engine è stato impostato.
+Fiber usa di default [html/template](https://pkg.go.dev/html/template/) quando nessun motore template è stato impostato.
 
-Se vuoi eseguire parzialmente o utilizzare un engine differente come [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) o [pug](https://github.com/Joker/jade) ecc..
+Se vuoi eseguire parzialmente o utilizzare un motore differente come [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) o [pug](https://github.com/Joker/jade) ecc..
 
-Dai un'occhiata al pacchetto [Template](https://github.com/gofiber/template) che supporta multipli view engine.
+Dai un'occhiata al pacchetto [Template](https://github.com/gofiber/template) che supporta multipli motore template.
 
 ```go
 package main
@@ -315,7 +315,7 @@ func main() {
 
 ### Raggruppare le route
 
-📖 [Group](https://docs.gofiber.io/api/app#group)
+📖 [Gruppi](https://docs.gofiber.io/api/app#group)
 
 ```go
 func middleware(c fiber.Ctx) error {
@@ -460,7 +460,7 @@ func main() {
 }
 ```
 
-### WebSocket Upgrade
+### WebSocket
 
 📖 [Websocket](https://github.com/gofiber/websocket)
 
@@ -494,7 +494,7 @@ func main() {
 }
 ```
 
-### Server-Sent Events
+### Eventi dal server
 
 📖 [More Info](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
 
@@ -535,7 +535,7 @@ func main() {
 }
 ```
 
-### Recover middleware
+### Recupera middleware
 
 📖 [Recover](https://docs.gofiber.io/api/middleware/recover)
 
@@ -560,7 +560,7 @@ func main() {
 
 </details>
 
-### Utilizzare Trusted Proxy
+### Utilizzare i Proxy fidati
 
 📖 [Config](https://docs.gofiber.io/api/fiber#config)
 
@@ -589,28 +589,29 @@ func main() {
 
 Qui una lista dei middleware inclusi con Fiber.
 
-| Middleware                                                                             | Descrizione                                                                                                                                                                  |
-| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth)         | Basic auth middleware provides an HTTP basic authentication. It calls the next handler for valid credentials and 401 Unauthorized for missing or invalid credentials.        |
-| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache)                 | Intercept and cache responses                                                                                                                                                |
-| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress)           | Compression middleware for Fiber, it supports `deflate`, `gzip` and `brotli` by default.                                                                                     |
-| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
-| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
-| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
-| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
-| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
-| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
-| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem)       | FileSystem middleware for Fiber, special thanks and credits to Alireza Salary                                                                                                |
-| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter)             | Rate-limiting middleware for Fiber. Use to limit repeated requests to public APIs and/or endpoints such as password reset.                                                   |
-| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger)               | HTTP request/response logger.                                                                                                                                                |
-| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor)             | Monitor middleware that reports server metrics, inspired by express-status-monitor                                                                                           |
-| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof)                 | Special thanks to Matthew Lee \(@mthli\)                                                                                                                                     |
-| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy)                 | Allows you to proxy requests to a multiple servers                                                                                                                           |
-| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover)             | Recover middleware recovers from panics anywhere in the stack chain and handles the control to the centralized[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). |
-| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid)         | Adds a requestid to every request.                                                                                                                                           |
-| [session](https://github.com/gofiber/fiber/tree/master/middleware/session)             | Session middleware. NOTE: This middleware uses our Storage package.                                                                                                          |
-| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip)                   | Skip middleware that skips a wrapped handler is a predicate is true.                                                                                                         |
-| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout)             | Adds a max time for a request and forwards to ErrorHandler if it is exceeded.                                                                                                |
+| Middleware                                                                             | Descrizione                                                                                                                                                                              |
+| :------------------------------------------------------------------------------------- |:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth)         | Middleware basico di autenticazione usando http. Chiama il suo handler se le credenziali sono guiste e il codice 401 Unauthorized per credenziali mancanti o invailde.                   |
+| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache)                 | Intercetta e mette nella cache la risposta                                                                                                                                               |
+| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress)           | Middlewere di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default.                                                                                                  |
+| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Ti permette di usare cross-origin resource sharing \(CORS\) con tante optzioni.                                                                                                          |
+| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Ti protegge da attachi CSRF.                                                                                                                                                             |
+| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware che encrypta i valori dei cookie.                                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Esporre le variabili di ambiente fornendo una configurazione facoltativa.                                                                                                                |
+| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | Middlewere che permette alle cache di essere piu efficienti e salvare banda, come un web server non deve rimandare il messagio pieno se il contenuto non è cambiato.                     |
+| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Middleware che serve via il suo runtime server HTTP varianti esposte in formato JSON.                                                                                                    |
+| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignora favicon dai logs o serve dalla memoria se un filepath si dà.                                                                                                                      |
+| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem)       | Middleware per il FileSystem per Fiber, grazie tante e crediti a Alireza Salary                                                                                                          |
+| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter)             | Middelwere per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset.                                                    |
+| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger)               | Logger HTTP per richiesta/risposta.                                                                                                                                                      |
+| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor)             | Middleware per monitorare che riporta metriche server, ispirato da express-status-monitor                                                                                                |
+| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof)                 | Grazie tante a Matthew Lee \(@mthli\)                                                                                                                                                    |
+| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy)                 | Ti permette di fare richieste proxy a multipli server.                                                                                                                                   |
+| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover)             | Middleware per recuperare dai attachi di panico da tutte le parti nella stack chain e da il controllo al centralizzato[ ErrorHandler](https://docs.gofiber.io/guide/error-handling).     |
+| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid)         | Aggiunge un requestid a ogni richiesta.                                                                                                                                                  |
+| [session](https://github.com/gofiber/fiber/tree/master/middleware/session)             | Middelwere per sessioni. NOTA: Questo middleware usa il nostro Storage package.                                                                                                          |
+| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip)                   | Middleware che salta un wrapped handler se un predicate è vero.                                                                                                                          |
+| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout)             | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera.                                                                                                      |
 
 ## 🧬 Middleware Esterni
 
@@ -618,26 +619,26 @@ La lista dei moduli middleware hostati esternamente e mantenuti dal [team di Fib
 
 | Middleware                                        | Descrizione                                                                                                                                                         |
 | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| [adaptor](https://github.com/gofiber/adaptor)     | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn!                                                                           |
-| [helmet](https://github.com/gofiber/helmet)       | Helps secure your apps by setting various HTTP headers.                                                                                                             |
-| [jwt](https://github.com/gofiber/jwt)             | JWT returns a JSON Web Token \(JWT\) auth middleware.                                                                                                               |
-| [keyauth](https://github.com/gofiber/keyauth)     | Key auth middleware provides a key based authentication.                                                                                                            |
-| [redirect](https://github.com/gofiber/redirect)   | Redirect middleware                                                                                                                                                 |
-| [rewrite](https://github.com/gofiber/rewrite)     | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. |
-| [storage](https://github.com/gofiber/storage)     | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares.                                                   |
-| [template](https://github.com/gofiber/template)   | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required.                                               |
-| [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support!                                                                                                          |
+| [adaptor](https://github.com/gofiber/adaptor)     | Converte gli handler net/http a/da i request handlers di Fiber, grazie tante a @arsmn!                                                                           |
+| [helmet](https://github.com/gofiber/helmet)       | Aiuta a mettere sicurezza alla tua app usando vari header HTTP.                                                                                                             |
+| [jwt](https://github.com/gofiber/jwt)             | Usa JSON Web Token \(JWT\) auth.                                                                                                               |
+| [keyauth](https://github.com/gofiber/keyauth)     | Usa auth basato su chiavi.                                                                                                            |
+| [redirect](https://github.com/gofiber/redirect)   | Middleware per reinderizzare                                                                                                                                                 |
+| [rewrite](https://github.com/gofiber/rewrite)     | Riscrive la path all URL con le regole date. Puo essere di aiuto per compatibilita o per creare link puliti e piu descrittivi. |
+| [storage](https://github.com/gofiber/storage)     | Dirver di storage che implementa la interfaccia Storage, fatto per essere usato con vari Fiber middleware.                                                   |
+| [template](https://github.com/gofiber/template)   | Questo pachetto contiene 8 motori template che possono essere usati con Fiber `v1.10.x`. Versione di go neccesaria: 1.13+.                                               |
+| [websocket](https://github.com/gofiber/websocket) | Basato su Fasthttp WebSocket per Fiber con supporto per Locals!                                                                                                          |
 
 ## 🕶️ Awesome List
 
-For more articles, middlewares, examples or tools check our [awesome list](https://github.com/gofiber/awesome-fiber).
+Per piu articoli, middlewares, esempi o attrezzi puoi usare la [awesome list](https://github.com/gofiber/awesome-fiber).
 
 ## 👍 Contribuire
 
 Se vuoi dirci **grazie** e/o supportare lo sviluppo di `Fiber`:
 
 1. Aggiungi una [stella GitHub](https://github.com/gofiber/fiber/stargazers) al progetto.
-2. Tweeta del progetto [su Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber).
+2. Twitta del progetto [su Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber).
 3. Scrivi una recensione o un tutorial su [Medium](https://medium.com/), [Dev.to](https://dev.to/) o sul tuo blog personale.
 4. Supporta il progetto donando una [tazza di caffè](https://buymeacoff.ee/fenny).
 
@@ -666,7 +667,7 @@ Fiber è un progetto open source che va avanti grazie alle donazioni per pagare
 | ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1    |
 | ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy)             | ☕ x 1    |
 
-## ‎‍💻 Code Contributor
+## ‎‍💻 Contributori
 
 <img src="https://opencollective.com/fiber/contributors.svg?width=890&button=false" alt="Code Contributors" style="max-width:100%;">
 
diff --git a/.github/README_ja.md b/.github/README_ja.md
index 43e80ee4..aa3c4794 100644
--- a/.github/README_ja.md
+++ b/.github/README_ja.md
@@ -147,7 +147,7 @@ Fiber は人気の高い Web フレームワークである Expressjs に**イ
 もしも、Web アプリケーションを Express 等の Node.js フレームワークで実装した経験があれば、多くの方法や原理がとても**馴染み深い**でしょう。
 
 ## ⚠️ 制限時効
-* Fiberはunsafeパッケージを使用しているため、最新のGoバージョンと互換性がない場合があります。Fiber 2.29.0 は、Go のバージョン 1.14 から 1.18 でテストされています。
+* Fiberはunsafeパッケージを使用しているため、最新のGoバージョンと互換性がない場合があります。Fiber 2.29.0 は、Go のバージョン 1.14 から 1.19 でテストされています。
 * Fiberはnet/httpインターフェースと互換性がありません。つまり、gqlgenやgo-swaggerなど、net/httpのエコシステムの一部であるプロジェクトを使用することができません。
 
 ## 👀 例
@@ -576,6 +576,7 @@ func main() {
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_ko.md b/.github/README_ko.md
index 189f1f23..ebe9ba30 100644
--- a/.github/README_ko.md
+++ b/.github/README_ko.md
@@ -144,7 +144,7 @@ Fiber는 인터넷에서 가장 인기있는 웹 프레임워크인 Express에
 우리는 **어떤한** 작업, **마감일정**, 개발자의 **기술**이던간에 **빠르고**, **유연하고**, **익숙한** Go 웹 프레임워크를 만들기 위해 사용자들의 [이슈들](https://github.com/gofiber/fiber/issues)을(그리고 모든 인터넷을 통해) **듣고 있습니다**! Express가 자바스크립트 세계에서 하는 것 처럼요.
 
 ## ⚠️ 한계점
-* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.29.0은 Go 버전 1.14에서 1.18로 테스트되고 있습니다.
+* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.29.0은 Go 버전 1.14에서 1.19로 테스트되고 있습니다.
 * Fiber는 net/http 인터페이스와 호환되지 않습니다.즉, gqlgen이나 go-swagger 등 net/http 생태계의 일부인 프로젝트를 사용할 수 없습니다.
 
 ## 👀 예제
@@ -577,6 +577,7 @@ Fiber 프레임워크에 포함되는 미들웨어 목록입니다.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_nl.md b/.github/README_nl.md
index cc68cdc3..a1f8e6c0 100644
--- a/.github/README_nl.md
+++ b/.github/README_nl.md
@@ -144,7 +144,7 @@ Fiber is **geïnspireerd** door Express, het populairste webframework op interne
 We **luisteren** naar onze gebruikers in [issues](https://github.com/gofiber/fiber/issues) (_en overal op het internet_) om een **snelle**, **flexibele** en **vriendelijk** Go web framework te maken voor **elke** taak, **deadline** en ontwikkelaar **vaardigheid**! Net zoals Express dat doet in de JavaScript-wereld.
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 Voorbeelden
@@ -577,6 +577,7 @@ Here is a list of middleware that are included within the Fiber framework.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_pt.md b/.github/README_pt.md
index b1b7ad4f..19e3cff3 100644
--- a/.github/README_pt.md
+++ b/.github/README_pt.md
@@ -142,7 +142,7 @@ Os novos gophers que mudaram do [Node.js](https://nodejs.org/en/about/) para o [
 O Fiber é **inspirado** no Express, o framework web mais popular da Internet. Combinamos a **facilidade** do Express e com o **desempenho bruto** do Go. Se você já implementou um aplicativo web com Node.js ( _usando Express.js ou similar_ ), então muitos métodos e princípios parecerão **muito familiares** para você.
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 Exemplos
@@ -571,6 +571,7 @@ Here is a list of middleware that are included within the Fiber framework.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_ru.md b/.github/README_ru.md
index 3d5539a5..8491b028 100644
--- a/.github/README_ru.md
+++ b/.github/README_ru.md
@@ -144,7 +144,7 @@ Fiber **вдохновлен** Express, самым популярным веб 
 Мы **прислушиваемся** к нашим пользователям в [issues](https://github.com/gofiber/fiber/issues), Discord [канале](https://gofiber.io/discord) _и в остальном Интернете_, чтобы создать **быстрый**, **гибкий** и **дружелюбный** веб фреймворк на Go для **любых** задач, **дедлайнов** и **уровней** разработчиков! Как это делает Express в мире JavaScript.
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 Примеры
@@ -578,6 +578,7 @@ func main() {
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_sa.md b/.github/README_sa.md
index 3406dbd5..676a08dc 100644
--- a/.github/README_sa.md
+++ b/.github/README_sa.md
@@ -158,7 +158,7 @@ Fiber هو **مستوحى** من Express, إطار الويب الأكثر شع
 ** و تطوير **مهارات**! فقط مثل Express تفعل لـ JavaScript عالم.
 
 ## ⚠️ Limitations
-* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18.
+* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19.
 * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
 
 ## 👀 أمثلة
@@ -642,6 +642,7 @@ Here is a list of middleware that are included within the Fiber framework.
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Enable cross-origin resource sharing \(CORS\) with various options.                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | Protect from CSRF exploits.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Ignore favicon from logs or serve from memory if a file path is provided.                                                                                                    |
diff --git a/.github/README_tr.md b/.github/README_tr.md
index c7f09000..5b15f068 100644
--- a/.github/README_tr.md
+++ b/.github/README_tr.md
@@ -143,7 +143,7 @@ Fiber, internet üzerinde en popüler web framework'ü olan Express'ten **esinle
 
 ## ⚠️ Sınırlamalar
 
--   Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.29.0, Go 1.14 ile 1.18 sürümleriyle test edildi.
+-   Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.29.0, Go 1.14 ile 1.19 sürümleriyle test edildi.
 -   Fiber net/http arabirimiyle uyumlu değildir. Yani gqlgen veya go-swagger gibi net/http ekosisteminin parçası olan projeleri kullanamazsınız.
 
 ## 👀 Örnekler
@@ -564,26 +564,27 @@ func main() {
 Fiber'a dahil edilen middlewareların bir listesi aşağıda verilmiştir.
 
 | Middleware                                                                             | Açıklama                                                                                                                                                                               |
-| :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| :------------------------------------------------------------------------------------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth)         | Basic auth middleware'ı, bir HTTP Basic auth sağlar. Geçerli kimlik bilgileri için sonraki handlerı ve eksik veya geçersiz kimlik bilgileri için 401 döndürür.                         |
-| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache)                 | Reponseları durdur ve önbelleğe al.                                                                                                                                                     |
+| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache)                 | Reponseları durdur ve önbelleğe al.                                                                                                                                                    |
 | [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress)           | Fiber için sıkıştırma middleware, varsayılan olarak `deflate`, `gzip` ve `brotli`yi destekler.                                                                                         |
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)                   | Çeşitli seçeneklerle başlangıçlar arası kaynak paylaşımını \(CORS\) etkinleştirin.                                                                                                     |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)                   | CSRF exploitlerinden korunun.                                                                                                                                                          |
-| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware'ı cookie değerlerini şifreler.                                                                                                                                       |
-| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware'ı sayfa içeriği değişmediyse bant genişliğini daha verimli kullanmak için tam sayfa içeriğini tekrar göndermez.                         |
-| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware, HTTP serverinin bazı runtime değişkenlerini JSON formatında sunar.                                                                                        |
+| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware'ı cookie değerlerini şifreler.                                                                                                                                      |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Environment değişkenlerini belirtilen ayarlara göre dışarıya açar.                                                                                                                     |
+| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware'ı sayfa içeriği değişmediyse bant genişliğini daha verimli kullanmak için tam sayfa içeriğini tekrar göndermez.                                                        |
+| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware, HTTP serverinin bazı runtime değişkenlerini JSON formatında sunar.                                                                                                  |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)             | Bir dosya yolu sağlanmışsa, loglardaki favicon'u yoksayar veya bellekten sunar.                                                                                                        |
-| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem)       | Fiber için FileSystem middleware, Alireza Salary'e özel teşekkürler.                                                                                                                    |
+| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem)       | Fiber için FileSystem middleware, Alireza Salary'e özel teşekkürler.                                                                                                                   |
 | [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter)             | Fiber için hız sınırlayıcı middleware'i. Açık API'lere ve/veya parola sıfırlama gibi endpointlere yönelik tekrarlanan istekleri sınırlamak için kullanın.                              |
 | [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger)               | HTTP istek/yanıt logger'ı.                                                                                                                                                             |
-| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor)             | Monitor middleware'ı sunucu metriklerini rapor eder, express-status-monitor'den esinlenildi.                                                                                                     |
-| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof)                 | Matthew Lee'ye özel teşekkürler \(@mthli\).                                                                                                                                             |
-| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy)                 | Birden çok sunucuya proxy istekleri yapmanızı sağlar.                                                                                                                                   |
+| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor)             | Monitor middleware'ı sunucu metriklerini rapor eder, express-status-monitor'den esinlenildi.                                                                                           |
+| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof)                 | Matthew Lee'ye özel teşekkürler \(@mthli\).                                                                                                                                            |
+| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy)                 | Birden çok sunucuya proxy istekleri yapmanızı sağlar.                                                                                                                                  |
 | [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover)             | Recover middleware'i, stack chain'ini herhangi bir yerindeki paniklerden kurtulur ve kontrolü merkezileştirilmiş [ErrorHandler'e](https://docs.gofiber.io/guide/error-handling) verir. |
-| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid)         | Her requeste id verir.                                                                                                                                                                  |
-| [session](https://github.com/gofiber/fiber/tree/master/middleware/session)             | Session için middleware. NOTE: Bu middleware Fiber'in Storage yapısını kullanır.                                                                                                                    |
-| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip)                   | Skip middleware'ı verilen koşul `true` olduğunda handler'ı atlar ve işlemez.                                                                                                                   |
+| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid)         | Her requeste id verir.                                                                                                                                                                 |
+| [session](https://github.com/gofiber/fiber/tree/master/middleware/session)             | Session için middleware. NOTE: Bu middleware Fiber'in Storage yapısını kullanır.                                                                                                       |
+| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip)                   | Skip middleware'ı verilen koşul `true` olduğunda handler'ı atlar ve işlemez.                                                                                                           |
 | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout)             | Bir request için maksimum süre ekler ve aşılırsa ErrorHandler'a iletir.                                                                                                                |
 
 ## 🧬 Harici Middlewarelar
diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md
index 1282cf7a..1b0b8a30 100644
--- a/.github/README_zh-CN.md
+++ b/.github/README_zh-CN.md
@@ -144,7 +144,7 @@ go get -u github.com/gofiber/fiber/v3
 我们会**倾听**用户在[issues](https://github.com/gofiber/fiber/issues)和 Discord [channel](https://gofiber.io/discord)和在互联网上的所有诉求，为了创建一个能让有着任何技术栈的开发者都能在deadline前完成任务的**迅速**，**灵活**以及**友好**的`Go web`框架，就像`Express`在`JavaScript`世界中一样。
 
 ## ⚠️ 限制
-* 由于 Fiber 使用了 unsafe 特性，导致其可能与最新的 Go 版本不兼容。Fiber 2.29.0 已经在 Go 1.14 到 1.18 上测试过。
+* 由于 Fiber 使用了 unsafe 特性，导致其可能与最新的 Go 版本不兼容。Fiber 2.29.0 已经在 Go 1.14 到 1.19 上测试过。
 * Fiber 与 net/http 接口不兼容。也就是说你无法使用 gqlen，go-swagger 或者任何其他属于 net/http 生态的项目。
 
 ## 👀 示例
@@ -573,6 +573,7 @@ func main() {
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)             | 使用各种选项启用跨源资源共享\(CORS\).                                                                                                          |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)             | 保护来自CSRF的漏洞.                                                                                                                                                  |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | 加密 cookie 值的加密中间件.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | 通过提供可选配置来公开环境变量。.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | 让缓存更加高效并且节省带宽,让web服务不再需要重新响应整个响应体如果响应内容未变更.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | 通过其HTTP服务器运行时间提供JSON格式的暴露变体.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)       | 如果提供了文件路径，则忽略日志中的图标或从内存中服务.                                                                                                    |
diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md
index 936768cc..a4db3dfd 100644
--- a/.github/README_zh-TW.md
+++ b/.github/README_zh-TW.md
@@ -144,7 +144,7 @@ Fiber **受到** 網路上最流行的 Web 框架 ExpressJS**啟發**，結合 E
 有什麼問題請發[issues](https://github.com/gofiber/fiber/issues)或加入 Discord [channel](https://gofiber.io/discord)討論，我們想要創造**快速**、**彈性**、**友善**的社群給**任何人**使用！就像 Express 那樣。
 
 ## 限制
-* 由於 Fiber 使用了 unsafe，該庫可能並不總是與最新的 Go 版本兼容。 Fiber 2.29.0 已經用 Go 版本 1.14 到 1.18 進行了測試。
+* 由於 Fiber 使用了 unsafe，該庫可能並不總是與最新的 Go 版本兼容。 Fiber 2.29.0 已經用 Go 版本 1.14 到 1.19 進行了測試。
 * Fiber 與 net/http 接口不兼容。 這意味著您將無法使用 gqlgen、go-swagger 或任何其他屬於 net/http 生態系統的項目。
 
 ## 👀 範例
@@ -573,6 +573,7 @@ func main() {
 | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors)             | 使用各種選項啟用跨域資源共享 \(CORS\)。                                                                                  |
 | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf)             | 防止 CSRF 漏洞利用。                                                                                                                          |
 | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values.                                                                                                                             |
+| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar)               | Expose environment variables with providing an optional config.                                                                                                              |
 | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag)                   | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.               |
 | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar)               | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format.                                                                               |
 | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon)       | 如果提供了文件路徑，則忽略日誌中的網站圖標或從內存中提供服務。                                                                                                    |
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
index 41fa29a7..6df74895 100644
--- a/.github/release-drafter.yml
+++ b/.github/release-drafter.yml
@@ -90,4 +90,6 @@ autolabeler:
 template: |
     $CHANGES
 
+    **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
+
     Thank you $CONTRIBUTORS for making this update possible.
diff --git a/app.go b/app.go
index 370cc5b2..708b16a1 100644
--- a/app.go
+++ b/app.go
@@ -119,16 +119,11 @@ type App struct {
 	// custom binders
 	customBinders []CustomBinder
 	// TLS handler
-	tlsHandler *tlsHandler
+	tlsHandler *TLSHandler
 }
 
 // Config is a struct holding the server settings.
 type Config struct {
-	// When set to true, this will spawn multiple Go processes listening on the same port.
-	//
-	// Default: false
-	Prefork bool `json:"prefork"`
-
 	// Enables the "Server: value" HTTP header.
 	//
 	// Default: ""
@@ -270,11 +265,6 @@ type Config struct {
 	// Default: false
 	DisableHeaderNormalizing bool `json:"disable_header_normalizing"`
 
-	// When set to true, it will not print out the «Fiber» ASCII art and listening address.
-	//
-	// Default: false
-	DisableStartupMessage bool `json:"disable_startup_message"`
-
 	// This function allows to setup app name for the app
 	//
 	// Default: nil
@@ -332,12 +322,6 @@ type Config struct {
 	// Default: xml.Marshal
 	XMLEncoder utils.XMLMarshal `json:"-"`
 
-	// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only)
-	// WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chose.
-	//
-	// Default: NetworkTCP4
-	Network string
-
 	// If you find yourself behind some sort of proxy, like a load balancer,
 	// then certain header information may be sent to you using special X-Forwarded-* headers or the Forwarded header.
 	// For example, the Host HTTP header is usually used to return the requested host.
@@ -349,12 +333,12 @@ type Config struct {
 	// If request ip in TrustedProxies whitelist then:
 	//   1. c.Scheme() get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header
 	//   2. c.IP() get value from ProxyHeader header.
-	//   3. c.Hostname() get value from X-Forwarded-Host header
+	//   3. c.Host() and c.Hostname() get value from X-Forwarded-Host header
 	// But if request ip NOT in Trusted Proxies whitelist then:
 	//   1. c.Scheme() WON't get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header,
 	//    will return https in case when tls connection is handled by the app, of http otherwise
 	//   2. c.IP() WON'T get value from ProxyHeader header, will return RemoteIP() from fasthttp context
-	//   3. c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host()
+	//   3. c.Host() and c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host()
 	//    will be used to get the hostname.
 	//
 	// Default: false
@@ -367,9 +351,12 @@ type Config struct {
 	trustedProxiesMap  map[string]struct{}
 	trustedProxyRanges []*net.IPNet
 
-	// If set to true, will print all routes with their method, path and handler.
+	// If set to true, c.IP() and c.IPs() will validate IP addresses before returning them.
+	// Also, c.IP() will return only the first valid IP rather than just the raw header
+	// WARNING: this has a performance cost associated with it.
+	//
 	// Default: false
-	EnablePrintRoutes bool `json:"enable_print_routes"`
+	EnableIPValidation bool `json:"enable_ip_validation"`
 
 	// You can define custom color scheme. They'll be used for startup message, route list and some middlewares.
 	//
@@ -526,9 +513,6 @@ func New(config ...Config) *App {
 	if app.config.XMLEncoder == nil {
 		app.config.XMLEncoder = xml.Marshal
 	}
-	if app.config.Network == "" {
-		app.config.Network = NetworkTCP4
-	}
 
 	app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies))
 	for _, ipAddress := range app.config.TrustedProxies {
@@ -575,6 +559,14 @@ func (app *App) RegisterCustomBinder(binder CustomBinder) {
 	app.customBinders = append(app.customBinders, binder)
 }
 
+// You can use SetTLSHandler to use ClientHelloInfo when using TLS with Listener.
+func (app *App) SetTLSHandler(tlsHandler *TLSHandler) {
+	// Attach the tlsHandler to the config
+	app.mutex.Lock()
+	app.tlsHandler = tlsHandler
+	app.mutex.Unlock()
+}
+
 // Mount attaches another app instance as a sub-router along a routing path.
 // It's very useful to split up a large API as many independent routers and
 // compose them as a single service using Mount. The fiber's error handler and
@@ -670,7 +662,7 @@ func (app *App) Use(args ...any) Router {
 // Get registers a route for GET methods that requests a representation
 // of the specified resource. Requests using GET should only retrieve data.
 func (app *App) Get(path string, handlers ...Handler) Router {
-	return app.Head(path, handlers...).Add(MethodGet, path, handlers...)
+	return app.Add(MethodGet, path, handlers...)
 }
 
 // Head registers a route for HEAD methods that asks for a response identical
@@ -756,17 +748,11 @@ func (app *App) Group(prefix string, handlers ...Handler) Router {
 
 // Route is used to define routes with a common prefix inside the common function.
 // Uses Group method to define new sub-router.
-func (app *App) Route(prefix string, fn func(router Router), name ...string) Router {
-	// Create new group
-	group := app.Group(prefix)
-	if len(name) > 0 {
-		group.Name(name[0])
-	}
-
-	// Define routes
-	fn(group)
+func (app *App) Route(path string) Register {
+	// Create new route
+	route := &Registering{app: app, path: path}
 
-	return group
+	return route
 }
 
 // Error makes it compatible with the `error` interface.
@@ -816,7 +802,8 @@ func (app *App) HandlersCount() uint32 {
 // Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
 func (app *App) Shutdown() error {
 	if app.hooks != nil {
-		defer app.hooks.executeOnShutdownHooks()
+		// TODO: check should be defered?
+		app.hooks.executeOnShutdownHooks()
 	}
 
 	app.mutex.Lock()
diff --git a/client.go b/client.go
index aab988ea..682c6d1d 100644
--- a/client.go
+++ b/client.go
@@ -740,9 +740,14 @@ func (a *Agent) RetryIf(retryIf RetryIfFunc) *Agent {
 /************************** End Agent Setting **************************/
 
 // Bytes returns the status code, bytes body and errors of url.
+//
+// it's not safe to use Agent after calling [Agent.Bytes]
 func (a *Agent) Bytes() (code int, body []byte, errs []error) {
 	defer a.release()
+	return a.bytes()
+}
 
+func (a *Agent) bytes() (code int, body []byte, errs []error) {
 	if errs = append(errs, a.errs...); len(errs) > 0 {
 		return
 	}
@@ -801,16 +806,22 @@ func printDebugInfo(req *Request, resp *Response, w io.Writer) {
 }
 
 // String returns the status code, string body and errors of url.
+//
+// it's not safe to use Agent after calling [Agent.String]
 func (a *Agent) String() (int, string, []error) {
-	code, body, errs := a.Bytes()
+	defer a.release()
+	code, body, errs := a.bytes()
 
 	return code, utils.UnsafeString(body), errs
 }
 
 // Struct returns the status code, bytes body and errors of url.
 // And bytes body will be unmarshalled to given v.
+//
+// it's not safe to use Agent after calling [Agent.Struct]
 func (a *Agent) Struct(v any) (code int, body []byte, errs []error) {
-	if code, body, errs = a.Bytes(); len(errs) > 0 {
+	defer a.release()
+	if code, body, errs = a.bytes(); len(errs) > 0 {
 		return
 	}
 
@@ -855,8 +866,12 @@ func (a *Agent) reset() {
 }
 
 var (
-	clientPool   sync.Pool
-	agentPool    sync.Pool
+	clientPool sync.Pool
+	agentPool  = sync.Pool{
+		New: func() interface{} {
+			return &Agent{req: &Request{}}
+		},
+	}
 	responsePool sync.Pool
 	argsPool     sync.Pool
 	formFilePool sync.Pool
@@ -894,11 +909,7 @@ func ReleaseClient(c *Client) {
 // no longer needed. This allows Agent recycling, reduces GC pressure
 // and usually improves performance.
 func AcquireAgent() *Agent {
-	v := agentPool.Get()
-	if v == nil {
-		return &Agent{req: &Request{}}
-	}
-	return v.(*Agent)
+	return agentPool.Get().(*Agent)
 }
 
 // ReleaseAgent returns a acquired via AcquireAgent to Agent pool.
diff --git a/ctx.go b/ctx.go
index 06fc2e67..5d6c57df 100644
--- a/ctx.go
+++ b/ctx.go
@@ -54,13 +54,13 @@ type DefaultCtx struct {
 	bind                *Bind                // Default bind reference
 }
 
-// tlsHandle object
-type tlsHandler struct {
+// TLSHandler object
+type TLSHandler struct {
 	clientHelloInfo *tls.ClientHelloInfo
 }
 
 // GetClientInfo Callback function to set CHI
-func (t *tlsHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
+func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
 	t.clientHelloInfo = info
 	return nil, nil
 }
@@ -210,7 +210,7 @@ func (c *DefaultCtx) BaseURL() string {
 	if c.baseURI != "" {
 		return c.baseURI
 	}
-	c.baseURI = c.Scheme() + "://" + c.Hostname()
+	c.baseURI = c.Scheme() + "://" + c.Host()
 	return c.baseURI
 }
 
@@ -468,11 +468,11 @@ func (c *DefaultCtx) GetRespHeader(key string, defaultValue ...string) string {
 	return defaultString(c.app.getString(c.fasthttp.Response.Header.Peek(key)), defaultValue)
 }
 
-// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header.
+// Host contains the host derived from the X-Forwarded-Host or Host HTTP header.
 // Returned value is only valid within the handler. Do not store any references.
 // Make copies or use the Immutable setting instead.
 // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
-func (c *DefaultCtx) Hostname() string {
+func (c *DefaultCtx) Host() string {
 	if c.IsProxyTrusted() {
 		if host := c.Get(HeaderXForwardedHost); len(host) > 0 {
 			return host
@@ -481,6 +481,16 @@ func (c *DefaultCtx) Hostname() string {
 	return c.app.getString(c.fasthttp.Request.URI().Host())
 }
 
+// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method.
+// Returned value is only valid within the handler. Do not store any references.
+// Make copies or use the Immutable setting instead.
+// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
+func (c *DefaultCtx) Hostname() string {
+	addr, _ := parseAddr(c.Host())
+
+	return addr
+}
+
 // Port returns the remote port of the request.
 func (c *DefaultCtx) Port() string {
 	port := c.fasthttp.RemoteAddr().(*net.TCPAddr).Port
@@ -488,33 +498,94 @@ func (c *DefaultCtx) Port() string {
 }
 
 // IP returns the remote IP address of the request.
+// If ProxyHeader and IP Validation is configured, it will parse that header and return the first valid IP address.
 // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
 func (c *DefaultCtx) IP() string {
 	if c.IsProxyTrusted() && len(c.app.config.ProxyHeader) > 0 {
-		return c.Get(c.app.config.ProxyHeader)
+		return c.extractIPFromHeader(c.app.config.ProxyHeader)
 	}
 
 	return c.fasthttp.RemoteIP().String()
 }
 
-// IPs returns an string slice of IP addresses specified in the X-Forwarded-For request header.
-func (c *DefaultCtx) IPs() (ips []string) {
-	header := c.fasthttp.Request.Header.Peek(HeaderXForwardedFor)
-	if len(header) == 0 {
-		return
+// validateIPIfEnabled will return the input IP when validation is disabled.
+// when validation is enabled, it will return an empty string if the input is not a valid IP.
+func (c *DefaultCtx) validateIPIfEnabled(ip string) string {
+	if c.app.config.EnableIPValidation && net.ParseIP(ip) == nil {
+		return ""
 	}
-	ips = make([]string, bytes.Count(header, []byte(","))+1)
-	var commaPos, i int
+	return ip
+}
+
+// extractIPsFromHeader will return a slice of IPs it found given a header name in the order they appear.
+// When IP validation is enabled, any invalid IPs will be omitted.
+func (c *DefaultCtx) extractIPsFromHeader(header string) (ipsFound []string) {
+	headerValue := c.Get(header)
+
+	// try to gather IPs in the input with minimal allocations to improve performance
+	ips := make([]string, bytes.Count([]byte(headerValue), []byte(","))+1)
+	var commaPos, i, validCount int
 	for {
-		commaPos = bytes.IndexByte(header, ',')
+		commaPos = bytes.IndexByte([]byte(headerValue), ',')
 		if commaPos != -1 {
-			ips[i] = strings.Trim(c.app.getString(header[:commaPos]), " ")
-			header, i = header[commaPos+1:], i+1
+			ips[i] = c.validateIPIfEnabled(strings.Trim(headerValue[:commaPos], " "))
+			if ips[i] != "" {
+				validCount++
+			}
+			headerValue, i = headerValue[commaPos+1:], i+1
 		} else {
-			ips[i] = strings.Trim(c.app.getString(header), " ")
-			return
+			ips[i] = c.validateIPIfEnabled(strings.Trim(headerValue, " "))
+			if ips[i] != "" {
+				validCount++
+			}
+			break
+		}
+	}
+
+	// filter out any invalid IP(s) that we found
+	if len(ips) == validCount {
+		ipsFound = ips
+	} else {
+		ipsFound = make([]string, validCount)
+		var validIndex int
+		for n := range ips {
+			if ips[n] != "" {
+				ipsFound[validIndex] = ips[n]
+				validIndex++
+			}
 		}
 	}
+	return
+}
+
+// extractIPFromHeader will attempt to pull the real client IP from the given header when IP validation is enabled.
+// currently, it will return the first valid IP address in header.
+// when IP validation is disabled, it will simply return the value of the header without any inspection.
+func (c *DefaultCtx) extractIPFromHeader(header string) string {
+	if c.app.config.EnableIPValidation {
+		// extract all IPs from the header's value
+		ips := c.extractIPsFromHeader(header)
+
+		// since X-Forwarded-For has no RFC, it's really up to the proxy to decide whether to append
+		// or prepend IPs to this list. For example, the AWS ALB will prepend but the F5 BIG-IP will append ;(
+		// for now lets just go with the first value in the list...
+		if len(ips) > 0 {
+			return ips[0]
+		}
+
+		// return the IP from the stack if we could not find any valid Ips
+		return c.fasthttp.RemoteIP().String()
+	}
+
+	// default behaviour if IP validation is not enabled is just to return whatever value is
+	// in the proxy header. Even if it is empty or invalid
+	return c.Get(c.app.config.ProxyHeader)
+}
+
+// IPs returns a string slice of IP addresses specified in the X-Forwarded-For request header.
+// When IP validation is enabled, only valid IPs are returned.
+func (c *DefaultCtx) IPs() (ips []string) {
+	return c.extractIPsFromHeader(HeaderXForwardedFor)
 }
 
 // Is returns the matching content type,
@@ -688,7 +759,7 @@ func (c *DefaultCtx) Params(key string, defaultValue ...string) string {
 		if len(key) != len(c.route.Params[i]) {
 			continue
 		}
-		if c.route.Params[i] == key {
+		if c.route.Params[i] == key || (!c.app.config.CaseSensitive && utils.EqualFold(c.route.Params[i], key)) {
 			// in case values are not here
 			if len(c.values) <= i || len(c.values[i]) == 0 {
 				break
@@ -855,20 +926,24 @@ func (c *DefaultCtx) BindVars(vars Map) error {
 func (c *DefaultCtx) getLocationFromRoute(route Route, params Map) (string, error) {
 	buf := bytebufferpool.Get()
 	for _, segment := range route.routeParser.segs {
+		if !segment.IsParam {
+			_, err := buf.WriteString(segment.Const)
+			if err != nil {
+				return "", err
+			}
+			continue
+		}
+
 		for key, val := range params {
-			if segment.IsParam && (key == segment.ParamName || (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters))) {
+			isSame := key == segment.ParamName || (!c.app.config.CaseSensitive && utils.EqualFold(key, segment.ParamName))
+			isGreedy := (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters))
+			if isSame || isGreedy {
 				_, err := buf.WriteString(utils.ToString(val))
 				if err != nil {
 					return "", err
 				}
 			}
 		}
-		if !segment.IsParam {
-			_, err := buf.WriteString(segment.Const)
-			if err != nil {
-				return "", err
-			}
-		}
 	}
 	location := buf.String()
 	// release buffer
@@ -1173,7 +1248,7 @@ func (c *DefaultCtx) Subdomains(offset ...int) []string {
 	if len(offset) > 0 {
 		o = offset[0]
 	}
-	subdomains := strings.Split(c.Hostname(), ".")
+	subdomains := strings.Split(c.Host(), ".")
 	l := len(subdomains) - o
 	// Check index to avoid slice bounds out of range panic
 	if l < 0 {
diff --git a/ctx_interface.go b/ctx_interface.go
index 9f24a4c4..3adcea49 100644
--- a/ctx_interface.go
+++ b/ctx_interface.go
@@ -123,7 +123,13 @@ type Ctx interface {
 	// Make copies or use the Immutable setting instead.
 	GetRespHeader(key string, defaultValue ...string) string
 
-	// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header.
+	// Host contains the host derived from the X-Forwarded-Host or Host HTTP header.
+	// Returned value is only valid within the handler. Do not store any references.
+	// Make copies or use the Immutable setting instead.
+	// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
+	Host() string
+
+	// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method.
 	// Returned value is only valid within the handler. Do not store any references.
 	// Make copies or use the Immutable setting instead.
 	// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
diff --git a/error.go b/error.go
index 87a6af38..6b08acef 100644
--- a/error.go
+++ b/error.go
@@ -2,19 +2,24 @@ package fiber
 
 import (
 	errors "encoding/json"
-	goErrors "errors"
+	stdErrors "errors"
 
 	"github.com/gofiber/fiber/v3/internal/schema"
 )
 
+// Graceful shutdown errors
+var (
+	ErrGracefulTimeout = stdErrors.New("shutdown: graceful timeout has been reached, exiting")
+)
+
 // Range errors
 var (
-	ErrRangeMalformed     = goErrors.New("range: malformed range header string")
-	ErrRangeUnsatisfiable = goErrors.New("range: unsatisfiable range")
+	ErrRangeMalformed     = stdErrors.New("range: malformed range header string")
+	ErrRangeUnsatisfiable = stdErrors.New("range: unsatisfiable range")
 )
 
 // Binder errors
-var ErrCustomBinderNotFound = goErrors.New("binder: custom binder not found, please be sure to enter the right name")
+var ErrCustomBinderNotFound = stdErrors.New("binder: custom binder not found, please be sure to enter the right name")
 
 // gorilla/schema errors
 type (
diff --git a/group.go b/group.go
index 8afd289d..1f09eca9 100644
--- a/group.go
+++ b/group.go
@@ -102,7 +102,7 @@ func (grp *Group) Use(args ...any) Router {
 // of the specified resource. Requests using GET should only retrieve data.
 func (grp *Group) Get(path string, handlers ...Handler) Router {
 	path = getGroupPath(grp.Prefix, path)
-	return grp.app.Add(MethodHead, path, handlers...).Add(MethodGet, path, handlers...)
+	return grp.app.Add(MethodGet, path, handlers...)
 }
 
 // Head registers a route for HEAD methods that asks for a response identical
@@ -184,15 +184,9 @@ func (grp *Group) Group(prefix string, handlers ...Handler) Router {
 
 // Route is used to define routes with a common prefix inside the common function.
 // Uses Group method to define new sub-router.
-func (grp *Group) Route(prefix string, fn func(router Router), name ...string) Router {
+func (grp *Group) Route(path string) Register {
 	// Create new group
-	group := grp.Group(prefix)
-	if len(name) > 0 {
-		group.Name(name[0])
-	}
-
-	// Define routes
-	fn(group)
+	register := &Registering{app: grp.app, path: getGroupPath(grp.Prefix, path)}
 
-	return group
+	return register
 }
diff --git a/helpers.go b/helpers.go
index a88ca0a3..a9b3a3f4 100644
--- a/helpers.go
+++ b/helpers.go
@@ -21,37 +21,6 @@ import (
 	"github.com/valyala/fasthttp"
 )
 
-/* #nosec */
-// lnMetadata will close the listener and return the addr and tls config
-func lnMetadata(network string, ln net.Listener) (addr string, cfg *tls.Config) {
-	// Get addr
-	addr = ln.Addr().String()
-
-	// Close listener
-	if err := ln.Close(); err != nil {
-		return
-	}
-
-	// Wait for the listener to be closed
-	var closed bool
-	for i := 0; i < 10; i++ {
-		conn, err := net.DialTimeout(network, addr, 3*time.Second)
-		if err != nil || conn == nil {
-			closed = true
-			break
-		}
-		_ = conn.Close()
-		time.Sleep(100 * time.Millisecond)
-	}
-	if !closed {
-		panic("listener: " + addr + ": Only one usage of each socket address (protocol/network address/port) is normally permitted.")
-	}
-
-	cfg = getTlsConfig(ln)
-
-	return
-}
-
 /* #nosec */
 // getTlsConfig returns a net listener's tls config
 func getTlsConfig(ln net.Listener) *tls.Config {
@@ -688,18 +657,21 @@ const (
 
 // Route Constraints
 const (
-	ConstraintInt        = "int"
-	ConstraintBool       = "bool"
-	ConstraintFloat      = "float"
-	ConstraintAlpha      = "alpha"
-	ConstraintGuid       = "guid"
-	ConstraintMinLen     = "minLen"
-	ConstraintMaxLen     = "maxLen"
-	ConstraintExactLen   = "exactLen"
-	ConstraintBetweenLen = "betweenLen"
-	ConstraintMin        = "min"
-	ConstraintMax        = "max"
-	ConstraintRange      = "range"
-	ConstraintDatetime   = "datetime"
-	ConstraintRegex      = "regex"
+	ConstraintInt             = "int"
+	ConstraintBool            = "bool"
+	ConstraintFloat           = "float"
+	ConstraintAlpha           = "alpha"
+	ConstraintGuid            = "guid"
+	ConstraintMinLen          = "minLen"
+	ConstraintMaxLen          = "maxLen"
+	ConstraintLen             = "len"
+	ConstraintBetweenLen      = "betweenLen"
+	ConstraintMinLenLower     = "minlen"
+	ConstraintMaxLenLower     = "maxlen"
+	ConstraintBetweenLenLower = "betweenlen"
+	ConstraintMin             = "min"
+	ConstraintMax             = "max"
+	ConstraintRange           = "range"
+	ConstraintDatetime        = "datetime"
+	ConstraintRegex           = "regex"
 )
diff --git a/internal/schema/decoder.go b/internal/schema/decoder.go
index 9d448222..b63c45e1 100644
--- a/internal/schema/decoder.go
+++ b/internal/schema/decoder.go
@@ -74,19 +74,19 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
 	}
 	v = v.Elem()
 	t := v.Type()
-	errors := MultiError{}
+	multiError := MultiError{}
 	for path, values := range src {
 		if parts, err := d.cache.parsePath(path, t); err == nil {
 			if err = d.decode(v, path, parts, values); err != nil {
-				errors[path] = err
+				multiError[path] = err
 			}
 		} else if !d.ignoreUnknownKeys {
-			errors[path] = UnknownKeyError{Key: path}
+			multiError[path] = UnknownKeyError{Key: path}
 		}
 	}
-	errors.merge(d.checkRequired(t, src))
-	if len(errors) > 0 {
-		return errors
+	multiError.merge(d.checkRequired(t, src))
+	if len(multiError) > 0 {
+		return multiError
 	}
 	return nil
 }
diff --git a/listen.go b/listen.go
index 95b0220e..1ecaa873 100644
--- a/listen.go
+++ b/listen.go
@@ -5,10 +5,11 @@
 package fiber
 
 import (
+	"context"
 	"crypto/tls"
 	"crypto/x509"
-	"errors"
 	"fmt"
+	"log"
 	"net"
 	"os"
 	"path/filepath"
@@ -23,173 +24,261 @@ import (
 	"github.com/mattn/go-isatty"
 )
 
-// Listener can be used to pass a custom listener.
-func (app *App) Listener(ln net.Listener) error {
-	// Prefork is supported for custom listeners
-	if app.config.Prefork {
-		addr, tlsConfig := lnMetadata(app.config.Network, ln)
-		return app.prefork(app.config.Network, addr, tlsConfig)
+// Figlet text to show Fiber ASCII art on startup message
+var figletFiberText = `
+    _______ __             
+   / ____(_) /_  ___  _____
+  / /_  / / __ \/ _ \/ ___/
+ / __/ / / /_/ /  __/ /    
+/_/   /_/_.___/\___/_/     %s`
+
+// ListenConfig is a struct to customize startup of Fiber.
+//
+// TODO: Add timeout for graceful shutdown.
+type ListenConfig struct {
+	// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only)
+	// WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chose.
+	//
+	// Default: NetworkTCP4
+	ListenerNetwork string `json:"listener_network"`
+
+	// CertFile is a path of certficate file.
+	// If you want to use TLS, you have to enter this field.
+	//
+	// Default : ""
+	CertFile string `json:"cert_file"`
+
+	// KeyFile is a path of certficate's private key.
+	// If you want to use TLS, you have to enter this field.
+	//
+	// Default : ""
+	CertKeyFile string `json:"cert_key_file"`
+
+	// CertClientFile is a path of client certficate.
+	// If you want to use mTLS, you have to enter this field.
+	//
+	// Default : ""
+	CertClientFile string `json:"cert_client_file"`
+
+	// GracefulContext is a field to shutdown Fiber by given context gracefully.
+	//
+	// Default: nil
+	GracefulContext context.Context `json:"graceful_context"`
+
+	// TLSConfigFunc allows customizing tls.Config as you want.
+	//
+	// Default: nil
+	TLSConfigFunc func(tlsConfig *tls.Config) `json:"tls_config_func"`
+
+	// ListenerFunc allows accessing and customizing net.Listener.
+	//
+	// Default: nil
+	ListenerAddrFunc func(addr net.Addr) `json:"listener_addr_func"`
+
+	// BeforeServeFunc allows customizing and accessing fiber app before serving the app.
+	//
+	// Default: nil
+	BeforeServeFunc func(app *App) error `json:"before_serve_func"`
+
+	// When set to true, it will not print out the «Fiber» ASCII art and listening address.
+	//
+	// Default: false
+	DisableStartupMessage bool `json:"disable_startup_message"`
+
+	// When set to true, this will spawn multiple Go processes listening on the same port.
+	//
+	// Default: false
+	EnablePrefork bool `json:"enable_prefork"`
+
+	// If set to true, will print all routes with their method, path and handler.
+	//
+	// Default: false
+	EnablePrintRoutes bool `json:"enable_print_routes"`
+
+	// OnShutdownError allows to customize error behavior when to graceful shutdown server by given signal.
+	//
+	// Default: Print error with log.Fatalf()
+	OnShutdownError func(err error)
+
+	// OnShutdownSuccess allows to customize success behavior when to graceful shutdown server by given signal.
+	//
+	// Default: nil
+	OnShutdownSuccess func()
+}
+
+// listenConfigDefault is a function to set default values of ListenConfig.
+func listenConfigDefault(config ...ListenConfig) ListenConfig {
+	if len(config) < 1 {
+		return ListenConfig{
+			ListenerNetwork: NetworkTCP4,
+			OnShutdownError: func(err error) {
+				log.Fatalf("shutdown: %v", err)
+			},
+		}
 	}
-	// prepare the server for the start
-	app.startupProcess()
-	// Print startup message
-	if !app.config.DisableStartupMessage {
-		app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "")
+
+	cfg := config[0]
+	if cfg.ListenerNetwork == "" {
+		cfg.ListenerNetwork = NetworkTCP4
 	}
-	// Print routes
-	if app.config.EnablePrintRoutes {
-		app.printRoutesMessage()
+
+	if cfg.OnShutdownError == nil {
+		cfg.OnShutdownError = func(err error) {
+			log.Fatalf("shutdown: %v", err)
+		}
 	}
-	// Start listening
-	return app.server.Serve(ln)
+
+	return cfg
 }
 
 // Listen serves HTTP requests from the given addr.
+// You should enter custom ListenConfig to customize startup. (TLS, mTLS, prefork...)
 //
 //	app.Listen(":8080")
 //	app.Listen("127.0.0.1:8080")
-func (app *App) Listen(addr string) error {
+//	app.Listen(":8080", ListenConfig{EnablePrefork: true})
+func (app *App) Listen(addr string, config ...ListenConfig) error {
+	cfg := listenConfigDefault(config...)
+
+	// Configure TLS
+	var tlsConfig *tls.Config = nil
+	if cfg.CertFile != "" && cfg.CertKeyFile != "" {
+		cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.CertKeyFile)
+		if err != nil {
+			return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", cfg.CertFile, cfg.CertKeyFile, err)
+		}
+
+		tlsHandler := &TLSHandler{}
+		tlsConfig = &tls.Config{
+			MinVersion: tls.VersionTLS12,
+			Certificates: []tls.Certificate{
+				cert,
+			},
+			GetCertificate: tlsHandler.GetClientInfo,
+		}
+
+		if cfg.CertClientFile != "" {
+			clientCACert, err := os.ReadFile(filepath.Clean(cfg.CertClientFile))
+			if err != nil {
+				return err
+			}
+
+			clientCertPool := x509.NewCertPool()
+			clientCertPool.AppendCertsFromPEM(clientCACert)
+
+			tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
+			tlsConfig.ClientCAs = clientCertPool
+		}
+
+		// Attach the tlsHandler to the config
+		app.SetTLSHandler(tlsHandler)
+	}
+
+	if cfg.TLSConfigFunc != nil {
+		cfg.TLSConfigFunc(tlsConfig)
+	}
+
+	// Graceful shutdown
+	if cfg.GracefulContext != nil {
+		ctx, cancel := context.WithCancel(cfg.GracefulContext)
+		defer cancel()
+
+		go app.gracefulShutdown(ctx, cfg)
+	}
+
 	// Start prefork
-	if app.config.Prefork {
-		return app.prefork(app.config.Network, addr, nil)
+	if cfg.EnablePrefork {
+		return app.prefork(addr, tlsConfig, cfg)
 	}
-	// Setup listener
-	ln, err := net.Listen(app.config.Network, addr)
+
+	// Configure Listener
+	ln, err := app.createListener(addr, tlsConfig, cfg)
 	if err != nil {
 		return err
 	}
+
 	// prepare the server for the start
 	app.startupProcess()
-	// Print startup message
-	if !app.config.DisableStartupMessage {
-		app.startupMessage(ln.Addr().String(), false, "")
-	}
-	// Print routes
-	if app.config.EnablePrintRoutes {
-		app.printRoutesMessage()
+
+	// Print startup message & routes
+	app.printMessages(cfg, ln)
+
+	// Serve
+	if cfg.BeforeServeFunc != nil {
+		if err := cfg.BeforeServeFunc(app); err != nil {
+			return err
+		}
 	}
-	// Start listening
+
 	return app.server.Serve(ln)
 }
 
-// ListenTLS serves HTTPS requests from the given addr.
-// certFile and keyFile are the paths to TLS certificate and key file:
-//
-//	app.ListenTLS(":8080", "./cert.pem", "./cert.key")
-func (app *App) ListenTLS(addr, certFile, keyFile string) error {
-	// Check for valid cert/key path
-	if len(certFile) == 0 || len(keyFile) == 0 {
-		return errors.New("tls: provide a valid cert or key path")
-	}
-	// Set TLS config with handler
-	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
-	if err != nil {
-		return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err)
-	}
-	tlsHandler := &tlsHandler{}
-	config := &tls.Config{
-		MinVersion: tls.VersionTLS12,
-		Certificates: []tls.Certificate{
-			cert,
-		},
-		GetCertificate: tlsHandler.GetClientInfo,
-	}
-	// Prefork is supported
-	if app.config.Prefork {
-		return app.prefork(app.config.Network, addr, config)
-	}
+// Listener serves HTTP requests from the given listener.
+// You should enter custom ListenConfig to customize startup. (prefork, startup message, graceful shutdown...)
+func (app *App) Listener(ln net.Listener, config ...ListenConfig) error {
+	cfg := listenConfigDefault(config...)
 
-	// Setup listener
-	ln, err := net.Listen(app.config.Network, addr)
-	ln = tls.NewListener(ln, config)
+	// Graceful shutdown
+	if cfg.GracefulContext != nil {
+		ctx, cancel := context.WithCancel(cfg.GracefulContext)
+		defer cancel()
 
-	if err != nil {
-		return err
+		go app.gracefulShutdown(ctx, cfg)
 	}
+
 	// prepare the server for the start
 	app.startupProcess()
-	// Print startup message
-	if !app.config.DisableStartupMessage {
-		app.startupMessage(ln.Addr().String(), true, "")
-	}
-	// Print routes
-	if app.config.EnablePrintRoutes {
-		app.printRoutesMessage()
-	}
-
-	// Attach the tlsHandler to the config
-	app.tlsHandler = tlsHandler
 
-	// Start listening
-	return app.server.Serve(ln)
-}
+	// Print startup message & routes
+	app.printMessages(cfg, ln)
 
-// ListenMutualTLS serves HTTPS requests from the given addr.
-// certFile, keyFile and clientCertFile are the paths to TLS certificate and key file:
-//
-//	app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem")
-func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error {
-	// Check for valid cert/key path
-	if len(certFile) == 0 || len(keyFile) == 0 {
-		return errors.New("tls: provide a valid cert or key path")
+	// Serve
+	if cfg.BeforeServeFunc != nil {
+		if err := cfg.BeforeServeFunc(app); err != nil {
+			return err
+		}
 	}
 
-	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
-	if err != nil {
-		return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err)
+	// Prefork is not supported for custom listeners
+	if cfg.EnablePrefork {
+		fmt.Println("[Warning] Prefork isn't supported for custom listeners.")
 	}
 
-	clientCACert, err := os.ReadFile(filepath.Clean(clientCertFile))
-	if err != nil {
-		return err
-	}
-	clientCertPool := x509.NewCertPool()
-	clientCertPool.AppendCertsFromPEM(clientCACert)
+	return app.server.Serve(ln)
+}
 
-	tlsHandler := &tlsHandler{}
-	config := &tls.Config{
-		MinVersion: tls.VersionTLS12,
-		ClientAuth: tls.RequireAndVerifyClientCert,
-		ClientCAs:  clientCertPool,
-		Certificates: []tls.Certificate{
-			cert,
-		},
-		GetCertificate: tlsHandler.GetClientInfo,
-	}
+// Create listener function.
+func (app *App) createListener(addr string, tlsConfig *tls.Config, cfg ListenConfig) (net.Listener, error) {
+	var listener net.Listener
+	var err error
 
-	// Prefork is supported
-	if app.config.Prefork {
-		return app.prefork(app.config.Network, addr, config)
+	if tlsConfig != nil {
+		listener, err = tls.Listen(cfg.ListenerNetwork, addr, tlsConfig)
+	} else {
+		listener, err = net.Listen(cfg.ListenerNetwork, addr)
 	}
 
-	// Setup listener
-	ln, err := tls.Listen(app.config.Network, addr, config)
-	if err != nil {
-		return err
+	if cfg.ListenerAddrFunc != nil {
+		cfg.ListenerAddrFunc(listener.Addr())
 	}
 
-	// prepare the server for the start
-	app.startupProcess()
+	return listener, err
+}
 
+func (app *App) printMessages(cfg ListenConfig, ln net.Listener) {
 	// Print startup message
-	if !app.config.DisableStartupMessage {
-		app.startupMessage(ln.Addr().String(), true, "")
+	if !cfg.DisableStartupMessage {
+		app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "", cfg)
 	}
 
 	// Print routes
-	if app.config.EnablePrintRoutes {
+	if cfg.EnablePrintRoutes {
 		app.printRoutesMessage()
 	}
-
-	// Attach the tlsHandler to the config
-	app.tlsHandler = tlsHandler
-
-	// Start listening
-	return app.server.Serve(ln)
 }
 
 // startupMessage prepares the startup message with the handler number, port, address and other information
-func (app *App) startupMessage(addr string, tls bool, pids string) {
+func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenConfig) {
 	// ignore child processes
 	if IsChild() {
 		return
@@ -198,55 +287,9 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
 	// Alias colors
 	colors := app.config.ColorScheme
 
-	value := func(s string, width int) string {
-		pad := width - len(s)
-		str := ""
-		for i := 0; i < pad; i++ {
-			str += "."
-		}
-		if s == "Disabled" {
-			str += " " + s
-		} else {
-			str += fmt.Sprintf(" %s%s%s", colors.Cyan, s, colors.Black)
-		}
-		return str
-	}
-
-	center := func(s string, width int) string {
-		pad := strconv.Itoa((width - len(s)) / 2)
-		str := fmt.Sprintf("%"+pad+"s", " ")
-		str += s
-		str += fmt.Sprintf("%"+pad+"s", " ")
-		if len(str) < width {
-			str += " "
-		}
-		return str
-	}
-
-	centerValue := func(s string, width int) string {
-		pad := strconv.Itoa((width - len([]rune(s))) / 2)
-		str := fmt.Sprintf("%"+pad+"s", " ")
-		str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black)
-		str += fmt.Sprintf("%"+pad+"s", " ")
-		if len([]rune(s))-10 < width && len([]rune(s))%2 == 0 {
-			// add an ending space if the length of str is even and str is not too long
-			str += " "
-		}
-		return str
-	}
-
-	pad := func(s string, width int) (str string) {
-		toAdd := width - len(s)
-		str += s
-		for i := 0; i < toAdd; i++ {
-			str += " "
-		}
-		return
-	}
-
 	host, port := parseAddr(addr)
 	if host == "" {
-		if app.config.Network == NetworkTCP6 {
+		if cfg.ListenerNetwork == NetworkTCP6 {
 			host = "[::1]"
 		} else {
 			host = "0.0.0.0"
@@ -259,50 +302,48 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
 	}
 
 	isPrefork := "Disabled"
-	if app.config.Prefork {
+	if cfg.EnablePrefork {
 		isPrefork = "Enabled"
 	}
 
 	procs := strconv.Itoa(runtime.GOMAXPROCS(0))
-	if !app.config.Prefork {
+	if !cfg.EnablePrefork {
 		procs = "1"
 	}
 
-	mainLogo := colors.Black + " ┌───────────────────────────────────────────────────┐\n"
-	if app.config.AppName != "" {
-		mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n"
+	out := colorable.NewColorableStdout()
+	if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) {
+		out = colorable.NewNonColorable(os.Stdout)
 	}
-	mainLogo += " │ " + centerValue("Fiber v"+Version, 49) + " │\n"
+
+	_, _ = fmt.Fprintf(out, "%s\n", fmt.Sprintf(figletFiberText, colors.Red+"v"+Version+colors.Reset))
+	_, _ = fmt.Fprintf(out, strings.Repeat("-", 50)+"\n")
 
 	if host == "0.0.0.0" {
-		mainLogo +=
-			" │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" +
-				" │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n"
+		_, _ = fmt.Fprintf(out,
+			"%sINFO%s Server started on %s%s://127.0.0.1:%s%s (bound on host 0.0.0.0 and port %s)\n",
+			colors.Green, colors.Reset, colors.Blue, scheme, port, colors.Reset, port)
+	} else {
+		_, _ = fmt.Fprintf(out,
+			"%sINFO%s Server started on %s%s%s\n",
+			colors.Green, colors.Reset, colors.Blue, fmt.Sprintf("%s://%s:%s", scheme, host, port), colors.Reset)
+	}
+
+	if app.config.AppName != "" {
+		_, _ = fmt.Fprintf(out, "%sINFO%s Application name: %s%s%s\n", colors.Green, colors.Reset, colors.Blue, app.config.AppName, colors.Reset)
+	}
+	_, _ = fmt.Fprintf(out,
+		"%sINFO%s Total handlers count: %s%s%s\n",
+		colors.Green, colors.Reset, colors.Blue, strconv.Itoa(int(app.handlersCount)), colors.Reset)
+	if isPrefork == "Enabled" {
+		_, _ = fmt.Fprintf(out, "%sINFO%s Prefork: %s%s%s\n", colors.Green, colors.Reset, colors.Blue, isPrefork, colors.Reset)
 	} else {
-		mainLogo +=
-			" │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n"
-	}
-
-	mainLogo += fmt.Sprintf(
-		" │                                                   │\n"+
-			" │ Handlers %s  Processes %s │\n"+
-			" │ Prefork .%s  PID ....%s │\n"+
-			" └───────────────────────────────────────────────────┘"+
-			colors.Reset,
-		value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12),
-		value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14),
-	)
-
-	var childPidsLogo string
-	if app.config.Prefork {
-		var childPidsTemplate string
-		childPidsTemplate += "%s"
-		childPidsTemplate += " ┌───────────────────────────────────────────────────┐\n%s"
-		childPidsTemplate += " └───────────────────────────────────────────────────┘"
-		childPidsTemplate += "%s"
-
-		newLine := " │ %s%s%s │"
+		_, _ = fmt.Fprintf(out, "%sINFO%s Prefork: %s%s%s\n", colors.Green, colors.Reset, colors.Red, isPrefork, colors.Reset)
+	}
+	_, _ = fmt.Fprintf(out, "%sINFO%s PID: %s%v%s\n", colors.Green, colors.Reset, colors.Blue, os.Getpid(), colors.Reset)
+	_, _ = fmt.Fprintf(out, "%sINFO%s Total process count: %s%s%s\n", colors.Green, colors.Reset, colors.Blue, procs, colors.Reset)
 
+	if cfg.EnablePrefork {
 		// Turn the `pids` variable (in the form ",a,b,c,d,e,f,etc") into a slice of PIDs
 		var pidSlice []string
 		for _, v := range strings.Split(pids, ",") {
@@ -311,77 +352,24 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
 			}
 		}
 
-		var lines []string
-		thisLine := "Child PIDs ... "
-		var itemsOnThisLine []string
-
-		addLine := func() {
-			lines = append(lines,
-				fmt.Sprintf(
-					newLine,
-					colors.Black,
-					thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)),
-					colors.Black,
-				),
-			)
-		}
-
-		for _, pid := range pidSlice {
-			if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 {
-				addLine()
-				thisLine = ""
-				itemsOnThisLine = []string{pid}
-			} else {
-				itemsOnThisLine = append(itemsOnThisLine, pid)
+		_, _ = fmt.Fprintf(out, "%sINFO%s Child PIDs: %s", colors.Green, colors.Reset, colors.Blue)
+		totalPids := len(pidSlice)
+		rowTotalPidCount := 10
+		for i := 0; i < totalPids; i += rowTotalPidCount {
+			start := i
+			end := i + rowTotalPidCount
+			if end > totalPids {
+				end = totalPids
 			}
+			for n, pid := range pidSlice[start:end] {
+				_, _ = fmt.Fprintf(out, "%s", pid)
+				if n+1 != len(pidSlice[start:end]) {
+					_, _ = fmt.Fprintf(out, ", ")
+				}
+			}
+			_, _ = fmt.Fprintf(out, "\n%s", colors.Reset)
 		}
-
-		// Add left over items to their own line
-		if len(itemsOnThisLine) != 0 {
-			addLine()
-		}
-
-		// Form logo
-		childPidsLogo = fmt.Sprintf(childPidsTemplate,
-			colors.Black,
-			strings.Join(lines, "\n")+"\n",
-			colors.Reset,
-		)
-	}
-
-	// Combine both the child PID logo and the main Fiber logo
-
-	// Pad the shorter logo to the length of the longer one
-	splitMainLogo := strings.Split(mainLogo, "\n")
-	splitChildPidsLogo := strings.Split(childPidsLogo, "\n")
-
-	mainLen := len(splitMainLogo)
-	childLen := len(splitChildPidsLogo)
-
-	if mainLen > childLen {
-		diff := mainLen - childLen
-		for i := 0; i < diff; i++ {
-			splitChildPidsLogo = append(splitChildPidsLogo, "")
-		}
-	} else {
-		diff := childLen - mainLen
-		for i := 0; i < diff; i++ {
-			splitMainLogo = append(splitMainLogo, "")
-		}
-	}
-
-	// Combine the two logos, line by line
-	output := "\n"
-	for i := range splitMainLogo {
-		output += colors.Black + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n"
-	}
-
-	out := colorable.NewColorableStdout()
-	if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) {
-		out = colorable.NewNonColorable(os.Stdout)
 	}
-
-	_, _ = fmt.Fprintln(out, output)
 }
 
 // printRoutesMessage print all routes with method, path, name and handlers
@@ -431,3 +419,16 @@ func (app *App) printRoutesMessage() {
 
 	_ = w.Flush()
 }
+
+// shutdown goroutine
+func (app *App) gracefulShutdown(ctx context.Context, cfg ListenConfig) {
+	<-ctx.Done()
+
+	if err := app.Shutdown(); err != nil {
+		cfg.OnShutdownError(err)
+	}
+
+	if success := cfg.OnShutdownSuccess; success != nil {
+		success()
+	}
+}
diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go
index 7ea8a7c8..c9253f0a 100644
--- a/middleware/cache/cache.go
+++ b/middleware/cache/cache.go
@@ -84,7 +84,7 @@ func New(config ...Config) fiber.Handler {
 	// Return new handler
 	return func(c fiber.Ctx) error {
 		// Only cache GET and HEAD methods
-		if c.Method() != fiber.MethodGet && c.Method() != fiber.MethodHead {
+		if c.Method() != fiber.MethodGet {
 			c.Set(cfg.CacheHeader, cacheUnreachable)
 			return c.Next()
 		}
diff --git a/middleware/csrf/README.md b/middleware/csrf/README.md
index a417881e..e25b8283 100644
--- a/middleware/csrf/README.md
+++ b/middleware/csrf/README.md
@@ -49,9 +49,12 @@ app.Use(csrf.New(csrf.Config{
 	CookieSameSite: "Lax",
 	Expiration:     1 * time.Hour,
 	KeyGenerator:   utils.UUID,
+	Extractor:      func(c *fiber.Ctx) (string, error) { ... },
 }))
 ```
 
+Note: KeyLookup will be ignored if Extractor is explicitly set.
+
 ### Custom Storage/Database
 
 You can use any storage from our [storage](https://github.com/gofiber/storage/) package.
@@ -74,7 +77,7 @@ type Config struct {
 	Next func(c fiber.Ctx) bool
 
 	// KeyLookup is a string in the form of "<source>:<key>" that is used
-	// to extract token from the request.
+	// to create an Extractor that extracts the token from the request.
 	// Possible values:
 	// - "header:<name>"
 	// - "query:<name>"
@@ -82,6 +85,8 @@ type Config struct {
 	// - "form:<name>"
 	// - "cookie:<name>"
 	//
+	// Ignored if an Extractor is explicitly set.
+	//
 	// Optional. Default: "header:X-CSRF-Token"
 	KeyLookup string
 
@@ -133,6 +138,13 @@ type Config struct {
 	//
 	// Optional. Default: utils.UUID
 	KeyGenerator func() string
+
+	// Extractor returns the csrf token
+	//
+	// If set this will be used in place of an Extractor based on KeyLookup.
+	//
+	// Optional. Default will create an Extractor based on KeyLookup.
+	Extractor func(c *fiber.Ctx) (string, error)
 }
 ```
 
diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go
index fbf6060b..994b91a6 100644
--- a/middleware/csrf/config.go
+++ b/middleware/csrf/config.go
@@ -17,7 +17,7 @@ type Config struct {
 	Next func(c fiber.Ctx) bool
 
 	// KeyLookup is a string in the form of "<source>:<key>" that is used
-	// to extract token from the request.
+	// to create an Extractor that extracts the token from the request.
 	// Possible values:
 	// - "header:<name>"
 	// - "query:<name>"
@@ -25,6 +25,8 @@ type Config struct {
 	// - "form:<name>"
 	// - "cookie:<name>"
 	//
+	// Ignored if an Extractor is explicitly set.
+	//
 	// Optional. Default: "header:X-CSRF-Token"
 	KeyLookup string
 
@@ -82,19 +84,25 @@ type Config struct {
 	// Optional. Default: DefaultErrorHandler
 	ErrorHandler fiber.ErrorHandler
 
-	// extractor returns the csrf token from the request based on KeyLookup
-	extractor func(c fiber.Ctx) (string, error)
+	// Extractor returns the csrf token
+	//
+	// If set this will be used in place of an Extractor based on KeyLookup.
+	//
+	// Optional. Default will create an Extractor based on KeyLookup.
+	Extractor func(c fiber.Ctx) (string, error)
 }
 
+const HeaderName = "X-Csrf-Token"
+
 // ConfigDefault is the default config
 var ConfigDefault = Config{
-	KeyLookup:      "header:X-Csrf-Token",
+	KeyLookup:      "header:" + HeaderName,
 	CookieName:     "csrf_",
 	CookieSameSite: "Lax",
 	Expiration:     1 * time.Hour,
 	KeyGenerator:   utils.UUID,
 	ErrorHandler:   defaultErrorHandler,
-	extractor:      csrfFromHeader("X-Csrf-Token"),
+	Extractor:      CsrfFromHeader(HeaderName),
 }
 
 // default ErrorHandler that process return error from fiber.Handler
@@ -139,18 +147,20 @@ func configDefault(config ...Config) Config {
 		panic("[CSRF] KeyLookup must in the form of <source>:<key>")
 	}
 
-	// By default we extract from a header
-	cfg.extractor = csrfFromHeader(textproto.CanonicalMIMEHeaderKey(selectors[1]))
-
-	switch selectors[0] {
-	case "form":
-		cfg.extractor = csrfFromForm(selectors[1])
-	case "query":
-		cfg.extractor = csrfFromQuery(selectors[1])
-	case "param":
-		cfg.extractor = csrfFromParam(selectors[1])
-	case "cookie":
-		cfg.extractor = csrfFromCookie(selectors[1])
+	if cfg.Extractor == nil {
+		// By default we extract from a header
+		cfg.Extractor = CsrfFromHeader(textproto.CanonicalMIMEHeaderKey(selectors[1]))
+
+		switch selectors[0] {
+		case "form":
+			cfg.Extractor = CsrfFromForm(selectors[1])
+		case "query":
+			cfg.Extractor = CsrfFromQuery(selectors[1])
+		case "param":
+			cfg.Extractor = CsrfFromParam(selectors[1])
+		case "cookie":
+			cfg.Extractor = CsrfFromCookie(selectors[1])
+		}
 	}
 
 	return cfg
diff --git a/middleware/csrf/csrf.go b/middleware/csrf/csrf.go
index 844da0f0..29512dab 100644
--- a/middleware/csrf/csrf.go
+++ b/middleware/csrf/csrf.go
@@ -39,7 +39,7 @@ func New(config ...Config) fiber.Handler {
 			// Assume that anything not defined as 'safe' by RFC7231 needs protection
 
 			// Extract token from client request i.e. header, query, param, form or cookie
-			token, err = cfg.extractor(c)
+			token, err = cfg.Extractor(c)
 			if err != nil {
 				return cfg.ErrorHandler(c, err)
 			}
diff --git a/middleware/csrf/extractors.go b/middleware/csrf/extractors.go
index c971ac48..0919dffd 100644
--- a/middleware/csrf/extractors.go
+++ b/middleware/csrf/extractors.go
@@ -15,7 +15,7 @@ var (
 )
 
 // csrfFromParam returns a function that extracts token from the url param string.
-func csrfFromParam(param string) func(c fiber.Ctx) (string, error) {
+func CsrfFromParam(param string) func(c fiber.Ctx) (string, error) {
 	return func(c fiber.Ctx) (string, error) {
 		token := c.Params(param)
 		if token == "" {
@@ -26,7 +26,7 @@ func csrfFromParam(param string) func(c fiber.Ctx) (string, error) {
 }
 
 // csrfFromForm returns a function that extracts a token from a multipart-form.
-func csrfFromForm(param string) func(c fiber.Ctx) (string, error) {
+func CsrfFromForm(param string) func(c fiber.Ctx) (string, error) {
 	return func(c fiber.Ctx) (string, error) {
 		token := c.FormValue(param)
 		if token == "" {
@@ -37,7 +37,7 @@ func csrfFromForm(param string) func(c fiber.Ctx) (string, error) {
 }
 
 // csrfFromCookie returns a function that extracts token from the cookie header.
-func csrfFromCookie(param string) func(c fiber.Ctx) (string, error) {
+func CsrfFromCookie(param string) func(c fiber.Ctx) (string, error) {
 	return func(c fiber.Ctx) (string, error) {
 		token := c.Cookies(param)
 		if token == "" {
@@ -48,7 +48,7 @@ func csrfFromCookie(param string) func(c fiber.Ctx) (string, error) {
 }
 
 // csrfFromHeader returns a function that extracts token from the request header.
-func csrfFromHeader(param string) func(c fiber.Ctx) (string, error) {
+func CsrfFromHeader(param string) func(c fiber.Ctx) (string, error) {
 	return func(c fiber.Ctx) (string, error) {
 		token := c.Get(param)
 		if token == "" {
@@ -59,7 +59,7 @@ func csrfFromHeader(param string) func(c fiber.Ctx) (string, error) {
 }
 
 // csrfFromQuery returns a function that extracts token from the query string.
-func csrfFromQuery(param string) func(c fiber.Ctx) (string, error) {
+func CsrfFromQuery(param string) func(c fiber.Ctx) (string, error) {
 	return func(c fiber.Ctx) (string, error) {
 		token := c.Query(param)
 		if token == "" {
diff --git a/middleware/csrf/manager.go b/middleware/csrf/manager.go
index 561117fe..3d1dc551 100644
--- a/middleware/csrf/manager.go
+++ b/middleware/csrf/manager.go
@@ -71,12 +71,14 @@ func (m *manager) getRaw(key string) (raw []byte) {
 // set data to storage or memory
 func (m *manager) set(key string, it *item, exp time.Duration) {
 	if raw, err := it.MarshalMsg(nil); err == nil {
+		// TODO: Should we use utils.CopyString(key) here?
 		_ = m.storage.Set(key, raw, exp)
 	}
 }
 
 // set data to storage or memory
 func (m *manager) setRaw(key string, raw []byte, exp time.Duration) {
+	// TODO: Should we use utils.CopyString(key) here?
 	_ = m.storage.Set(key, raw, exp)
 }
 
diff --git a/middleware/envvar/README.md b/middleware/envvar/README.md
new file mode 100644
index 00000000..a16923d0
--- /dev/null
+++ b/middleware/envvar/README.md
@@ -0,0 +1,83 @@
+# Exposing Environment Variables Middleware
+
+EnvVar middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to expose environment variables with various options.
+
+## Table of Contents
+
+- [Environment Variables (EnvVar) Middleware](#environment-variables-envvar-middleware)
+    - [Table of Contents](#table-of-contents)
+    - [Signatures](#signatures)
+    - [Examples](#examples)
+        - [Default Config](#default-config)
+        - [Custom Config](#custom-config)
+        - [Response](#response)
+    - [Config](#config)
+    - [Default Config](#default-config-1)
+
+## Signatures
+
+```go
+func New(config ...Config) fiber.Handler
+```
+
+## Examples
+
+First import the middleware from Fiber,
+
+```go
+import (
+  "github.com/gofiber/fiber/v2"
+  "github.com/gofiber/fiber/v2/middleware/envvar"
+)
+```
+
+Then create a Fiber app with `app := fiber.New()`.
+
+**Note**: You need to provide a path to use envvar middleware.
+
+### Default Config
+
+```go
+app.Use("/expose/envvars", envvar.New())
+```
+
+### Custom Config
+
+```go
+app.Use("/expose/envvars", envvar.New(
+	envvar.Config{
+		ExportVars:  map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"},
+		ExcludeVars: map[string]string{"excludeKey": ""},
+	}),
+)
+```
+
+### Response
+
+Http response contract:
+```
+{
+  "vars": {
+    "someEnvVariable": "someValue",
+    "anotherEnvVariable": "anotherValue"
+  }
+}
+```
+
+## Config
+
+```go
+// Config defines the config for middleware.
+type Config struct {
+	// ExportVars specifies the environment variables that should export
+	ExportVars map[string]string
+	// ExcludeVars specifies the environment variables that should not export
+	ExcludeVars map[string]string
+}
+```
+
+## Default Config
+
+```go
+Config{}
+```
diff --git a/middleware/envvar/envvar.go b/middleware/envvar/envvar.go
new file mode 100644
index 00000000..47cd93f1
--- /dev/null
+++ b/middleware/envvar/envvar.go
@@ -0,0 +1,69 @@
+package envvar
+
+import (
+	"os"
+	"strings"
+
+	"github.com/gofiber/fiber/v3"
+)
+
+// Config defines the config for middleware.
+type Config struct {
+	// ExportVars specifies the environment variables that should export
+	ExportVars map[string]string
+	// ExcludeVars specifies the environment variables that should not export
+	ExcludeVars map[string]string
+}
+
+type EnvVar struct {
+	Vars map[string]string `json:"vars"`
+}
+
+func (envVar *EnvVar) set(key, val string) {
+	envVar.Vars[key] = val
+}
+
+var defaultConfig = Config{}
+
+func New(config ...Config) fiber.Handler {
+	var cfg = defaultConfig
+	if len(config) > 0 {
+		cfg = config[0]
+	}
+
+	return func(c fiber.Ctx) error {
+		if c.Method() != fiber.MethodGet {
+			return fiber.ErrMethodNotAllowed
+		}
+
+		envVar := newEnvVar(cfg)
+		varsByte, err := c.App().Config().JSONEncoder(envVar)
+		if err != nil {
+			return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
+		}
+		c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8)
+		return c.Send(varsByte)
+	}
+}
+
+func newEnvVar(cfg Config) *EnvVar {
+	vars := &EnvVar{Vars: make(map[string]string)}
+
+	if len(cfg.ExportVars) > 0 {
+		for key, defaultVal := range cfg.ExportVars {
+			vars.set(key, defaultVal)
+			if envVal, exists := os.LookupEnv(key); exists {
+				vars.set(key, envVal)
+			}
+		}
+	} else {
+		for _, envVal := range os.Environ() {
+			keyVal := strings.SplitN(envVal, "=", 2)
+			if _, exists := cfg.ExcludeVars[keyVal[0]]; !exists {
+				vars.set(keyVal[0], keyVal[1])
+			}
+		}
+	}
+
+	return vars
+}
diff --git a/middleware/logger/README.md b/middleware/logger/README.md
index 3bc9e758..c77d4f6a 100644
--- a/middleware/logger/README.md
+++ b/middleware/logger/README.md
@@ -11,6 +11,7 @@ Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP r
 		- [Logging Request ID](#logging-request-id)
 		- [Changing TimeZone & TimeFormat](#changing-timezone--timeformat)
 		- [Custom File Writer](#custom-file-writer)
+		- [Logging with Zerolog](#logging-with-zerolog)
 	- [Config](#config)
 	- [Default Config](#default-config-1)
 	- [Constants](#constants)
@@ -76,6 +77,43 @@ app.Use(logger.New(logger.Config{
 }))
 ```
 
+### Logging with Zerolog
+```go
+package main
+
+import (
+	"os"
+
+	"github.com/gofiber/fiber/v3"
+	"github.com/gofiber/fiber/v3/middleware/logger"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
+)
+
+func main() {
+	app := fiber.New()
+
+	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
+
+	app.Use(logger.New(logger.Config{LoggerFunc: func(c fiber.Ctx, data *logger.LoggerData, cfg logger.Config) error {
+		log.Info().
+			Str("path", c.Path()).
+			Str("method", c.Method()).
+			Int("status", c.Response().
+				StatusCode()).
+			Msg("new request")
+
+		return nil
+	}}))
+
+	app.Get("/", func(c fiber.Ctx) error {
+		return c.SendString("test")
+	})
+
+	app.Listen(":3000")
+}
+```
+
 ## Config
 ```go
 // Config defines the config for middleware.
@@ -109,19 +147,38 @@ type Config struct {
 	//
 	// Default: os.Stderr
 	Output io.Writer
+
+	// You can define specific things before the returning the handler: colors, template, etc.
+	//
+	// Optional. Default: beforeHandlerFunc
+	BeforeHandlerFunc func(Config)
+
+	// You can use custom loggers with Fiber by using this field.
+	// This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc.
+	// If you don't define anything for this field, it'll use classical logger of Fiber.
+	//
+	// Optional. Default: defaultLogger
+	LoggerFunc func(c fiber.Ctx, data *LoggerData, cfg Config) error
 }
 ```
 
 ## Default Config
 ```go
+// ConfigDefault is the default config
 var ConfigDefault = Config{
-	Next:         nil,
-	Format:       "[${time}] ${status} - ${latency} ${method} ${path}\n",
-	TimeFormat:   "15:04:05",
-	TimeZone:     "Local",
-	TimeInterval: 500 * time.Millisecond,
-	Output:       os.Stderr,
+	Next:              nil,
+	Format:            defaultFormat,
+	TimeFormat:        "15:04:05",
+	TimeZone:          "Local",
+	TimeInterval:      500 * time.Millisecond,
+	Output:            os.Stdout,
+	BeforeHandlerFunc: beforeHandlerFunc,
+	LoggerFunc:        defaultLogger,
+	enableColors:      true,
 }
+
+// default logging format for Fiber's default logger
+var defaultFormat = "[${time}] ${status} - ${latency} ${method} ${path}\n"
 ```
 
 ## Constants
diff --git a/middleware/logger/config.go b/middleware/logger/config.go
index b8e6d4df..700f2c69 100644
--- a/middleware/logger/config.go
+++ b/middleware/logger/config.go
@@ -41,6 +41,18 @@ type Config struct {
 	// Default: os.Stdout
 	Output io.Writer
 
+	// You can define specific things before the returning the handler: colors, template, etc.
+	//
+	// Optional. Default: beforeHandlerFunc
+	BeforeHandlerFunc func(Config)
+
+	// You can use custom loggers with Fiber by using this field.
+	// This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc.
+	// If you don't define anything for this field, it'll use default logger of Fiber.
+	//
+	// Optional. Default: defaultLogger
+	LoggerFunc func(c fiber.Ctx, data *LoggerData, cfg Config) error
+
 	enableColors     bool
 	enableLatency    bool
 	timeZoneLocation *time.Location
@@ -48,15 +60,20 @@ type Config struct {
 
 // ConfigDefault is the default config
 var ConfigDefault = Config{
-	Next:         nil,
-	Format:       "[${time}] ${status} - ${latency} ${method} ${path}\n",
-	TimeFormat:   "15:04:05",
-	TimeZone:     "Local",
-	TimeInterval: 500 * time.Millisecond,
-	Output:       os.Stdout,
-	enableColors: true,
+	Next:              nil,
+	Format:            defaultFormat,
+	TimeFormat:        "15:04:05",
+	TimeZone:          "Local",
+	TimeInterval:      500 * time.Millisecond,
+	Output:            os.Stdout,
+	BeforeHandlerFunc: beforeHandlerFunc,
+	LoggerFunc:        defaultLogger,
+	enableColors:      true,
 }
 
+// default logging format for Fiber's default logger
+var defaultFormat = "[${time}] ${status} - ${latency} ${method} ${path}\n"
+
 // Function to check if the logger format is compatible for coloring
 func validCustomFormat(format string) bool {
 	validTemplates := []string{"${status}", "${method}"}
@@ -105,5 +122,14 @@ func configDefault(config ...Config) Config {
 	if cfg.Output == nil {
 		cfg.Output = ConfigDefault.Output
 	}
+
+	if cfg.BeforeHandlerFunc == nil {
+		cfg.BeforeHandlerFunc = ConfigDefault.BeforeHandlerFunc
+	}
+
+	if cfg.LoggerFunc == nil {
+		cfg.LoggerFunc = ConfigDefault.LoggerFunc
+	}
+
 	return cfg
 }
diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go
new file mode 100644
index 00000000..7565f3dc
--- /dev/null
+++ b/middleware/logger/default_logger.go
@@ -0,0 +1,221 @@
+package logger
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"time"
+
+	"github.com/gofiber/fiber/v3"
+	"github.com/mattn/go-colorable"
+	"github.com/mattn/go-isatty"
+	"github.com/valyala/bytebufferpool"
+	"github.com/valyala/fasthttp"
+	"github.com/valyala/fasttemplate"
+)
+
+// LoggerData is a struct to define some variables to use in custom logger function.
+type LoggerData struct {
+	mu            sync.Mutex
+	Pid           string
+	ErrPaddingStr string
+	ChainErr      error
+	Start         time.Time
+	Stop          time.Time
+	Timestamp     atomic.Value
+}
+
+var tmpl *fasttemplate.Template
+
+// default logger for fiber
+func defaultLogger(c fiber.Ctx, data *LoggerData, cfg Config) error {
+	// Alias colors
+	colors := c.App().Config().ColorScheme
+
+	// Get new buffer
+	buf := bytebufferpool.Get()
+
+	// Default output when no custom Format or io.Writer is given
+	if cfg.enableColors && cfg.Format == defaultFormat {
+		// Format error if exist
+		formatErr := ""
+		if data.ChainErr != nil {
+			formatErr = colors.Red + " | " + data.ChainErr.Error() + colors.Reset
+		}
+
+		// Format log to buffer
+		_, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+data.ErrPaddingStr+"s %s\n",
+			data.Timestamp.Load().(string),
+			statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset,
+			data.Stop.Sub(data.Start).Round(time.Millisecond),
+			c.IP(),
+			methodColor(c.Method(), colors), c.Method(), colors.Reset,
+			c.Path(),
+			formatErr,
+		))
+
+		// Write buffer to output
+		_, _ = cfg.Output.Write(buf.Bytes())
+
+		// Put buffer back to pool
+		bytebufferpool.Put(buf)
+
+		// End chain
+		return nil
+	}
+
+	// Loop over template tags to replace it with the correct value
+	_, err := tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
+		switch tag {
+		case TagTime:
+			return buf.WriteString(data.Timestamp.Load().(string))
+		case TagReferer:
+			return buf.WriteString(c.Get(fiber.HeaderReferer))
+		case TagProtocol:
+			return buf.WriteString(c.Protocol())
+		case TagScheme:
+			return buf.WriteString(c.Scheme())
+		case TagPid:
+			return buf.WriteString(data.Pid)
+		case TagPort:
+			return buf.WriteString(c.Port())
+		case TagIP:
+			return buf.WriteString(c.IP())
+		case TagIPs:
+			return buf.WriteString(c.Get(fiber.HeaderXForwardedFor))
+		case TagHost:
+			return buf.WriteString(c.Host())
+		case TagPath:
+			return buf.WriteString(c.Path())
+		case TagURL:
+			return buf.WriteString(c.OriginalURL())
+		case TagUA:
+			return buf.WriteString(c.Get(fiber.HeaderUserAgent))
+		case TagLatency:
+			return buf.WriteString(fmt.Sprintf("%7v", data.Stop.Sub(data.Start).Round(time.Millisecond)))
+		case TagBody:
+			return buf.Write(c.Body())
+		case TagBytesReceived:
+			return appendInt(buf, len(c.Request().Body()))
+		case TagBytesSent:
+			return appendInt(buf, len(c.Response().Body()))
+		case TagRoute:
+			return buf.WriteString(c.Route().Path)
+		case TagStatus:
+			if cfg.enableColors {
+				return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset))
+			}
+			return appendInt(buf, c.Response().StatusCode())
+		case TagResBody:
+			return buf.Write(c.Response().Body())
+		case TagReqHeaders:
+			out := make(map[string]string, 0)
+			if err := c.Bind().Header(&out); err != nil {
+				return 0, err
+			}
+
+			reqHeaders := make([]string, 0)
+			for k, v := range out {
+				reqHeaders = append(reqHeaders, k+"="+v)
+			}
+			return buf.Write([]byte(strings.Join(reqHeaders, "&")))
+		case TagQueryStringParams:
+			return buf.WriteString(c.Request().URI().QueryArgs().String())
+		case TagMethod:
+			if cfg.enableColors {
+				return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset))
+			}
+			return buf.WriteString(c.Method())
+		case TagBlack:
+			return buf.WriteString(colors.Black)
+		case TagRed:
+			return buf.WriteString(colors.Red)
+		case TagGreen:
+			return buf.WriteString(colors.Green)
+		case TagYellow:
+			return buf.WriteString(colors.Yellow)
+		case TagBlue:
+			return buf.WriteString(colors.Blue)
+		case TagMagenta:
+			return buf.WriteString(colors.Magenta)
+		case TagCyan:
+			return buf.WriteString(colors.Cyan)
+		case TagWhite:
+			return buf.WriteString(colors.White)
+		case TagReset:
+			return buf.WriteString(colors.Reset)
+		case TagError:
+			if data.ChainErr != nil {
+				return buf.WriteString(data.ChainErr.Error())
+			}
+			return buf.WriteString("-")
+		default:
+			// Check if we have a value tag i.e.: "reqHeader:x-key"
+			switch {
+			case strings.HasPrefix(tag, TagReqHeader):
+				return buf.WriteString(c.Get(tag[10:]))
+			case strings.HasPrefix(tag, TagRespHeader):
+				return buf.WriteString(c.GetRespHeader(tag[11:]))
+			case strings.HasPrefix(tag, TagQuery):
+				return buf.WriteString(c.Query(tag[6:]))
+			case strings.HasPrefix(tag, TagForm):
+				return buf.WriteString(c.FormValue(tag[5:]))
+			case strings.HasPrefix(tag, TagCookie):
+				return buf.WriteString(c.Cookies(tag[7:]))
+			case strings.HasPrefix(tag, TagLocals):
+				switch v := c.Locals(tag[7:]).(type) {
+				case []byte:
+					return buf.Write(v)
+				case string:
+					return buf.WriteString(v)
+				case nil:
+					return 0, nil
+				default:
+					return buf.WriteString(fmt.Sprintf("%v", v))
+				}
+			}
+		}
+		return 0, nil
+	})
+	// Also write errors to the buffer
+	if err != nil {
+		_, _ = buf.WriteString(err.Error())
+	}
+	data.mu.Lock()
+	// Write buffer to output
+	if _, err := cfg.Output.Write(buf.Bytes()); err != nil {
+		// Write error to output
+		if _, err := cfg.Output.Write([]byte(err.Error())); err != nil {
+			// There is something wrong with the given io.Writer
+			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
+		}
+	}
+	data.mu.Unlock()
+	// Put buffer back to pool
+	bytebufferpool.Put(buf)
+
+	return nil
+}
+
+// run something before returning the handler
+func beforeHandlerFunc(cfg Config) {
+	// Create template parser
+	tmpl = fasttemplate.New(cfg.Format, "${", "}")
+
+	// If colors are enabled, check terminal compatibility
+	if cfg.enableColors {
+		cfg.Output = colorable.NewColorableStdout()
+		if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) {
+			cfg.Output = colorable.NewNonColorable(os.Stdout)
+		}
+	}
+}
+
+func appendInt(buf *bytebufferpool.ByteBuffer, v int) (int, error) {
+	old := len(buf.B)
+	buf.B = fasthttp.AppendUint(buf.B, v)
+	return len(buf.B) - old, nil
+}
diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go
index 72f78764..98f68945 100644
--- a/middleware/logger/logger.go
+++ b/middleware/logger/logger.go
@@ -1,8 +1,6 @@
 package logger
 
 import (
-	"fmt"
-	"io"
 	"os"
 	"strconv"
 	"strings"
@@ -11,11 +9,6 @@ import (
 	"time"
 
 	"github.com/gofiber/fiber/v3"
-	"github.com/mattn/go-colorable"
-	"github.com/mattn/go-isatty"
-	"github.com/valyala/bytebufferpool"
-	"github.com/valyala/fasthttp"
-	"github.com/valyala/fasttemplate"
 )
 
 // Logger variables
@@ -76,9 +69,6 @@ func New(config ...Config) fiber.Handler {
 	// Check if format contains latency
 	cfg.enableLatency = strings.Contains(cfg.Format, "${latency}")
 
-	// Create template parser
-	tmpl := fasttemplate.New(cfg.Format, "${", "}")
-
 	// Create correct timeformat
 	var timestamp atomic.Value
 	timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat))
@@ -98,20 +88,25 @@ func New(config ...Config) fiber.Handler {
 
 	// Set variables
 	var (
-		once       sync.Once
 		mu         sync.Mutex
+		once       sync.Once
 		errHandler fiber.ErrorHandler
 	)
 
-	// If colors are enabled, check terminal compatibility
-	if cfg.enableColors {
-		cfg.Output = colorable.NewColorableStdout()
-		if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) {
-			cfg.Output = colorable.NewNonColorable(os.Stdout)
-		}
-	}
+	// Err padding
 	errPadding := 15
 	errPaddingStr := strconv.Itoa(errPadding)
+
+	// Before handling func
+	cfg.BeforeHandlerFunc(cfg)
+
+	// Logger data
+	data := &LoggerData{
+		Pid:           pid,
+		ErrPaddingStr: errPaddingStr,
+		Timestamp:     timestamp,
+	}
+
 	// Return new handler
 	return func(c fiber.Ctx) (err error) {
 		// Don't execute middleware if Next returns true
@@ -119,9 +114,6 @@ func New(config ...Config) fiber.Handler {
 			return c.Next()
 		}
 
-		// Alias colors
-		colors := c.App().Config().ColorScheme
-
 		// Set error handler once
 		once.Do(func() {
 			// get longested possible path
@@ -160,174 +152,17 @@ func New(config ...Config) fiber.Handler {
 			stop = time.Now()
 		}
 
-		// Get new buffer
-		buf := bytebufferpool.Get()
-
-		// Default output when no custom Format or io.Writer is given
-		if cfg.enableColors && cfg.Format == ConfigDefault.Format {
-			// Format error if exist
-			formatErr := ""
-			if chainErr != nil {
-				formatErr = colors.Red + " | " + chainErr.Error() + colors.Reset
-			}
-
-			// Format log to buffer
-			_, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n",
-				timestamp.Load().(string),
-				statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset,
-				stop.Sub(start).Round(time.Millisecond),
-				c.IP(),
-				methodColor(c.Method(), colors), c.Method(), colors.Reset,
-				c.Path(),
-				formatErr,
-			))
-
-			// Write buffer to output
-			_, _ = cfg.Output.Write(buf.Bytes())
-
-			// Put buffer back to pool
-			bytebufferpool.Put(buf)
-
-			// End chain
-			return nil
-		}
-
-		// Loop over template tags to replace it with the correct value
-		_, err = tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
-			switch tag {
-			case TagTime:
-				return buf.WriteString(timestamp.Load().(string))
-			case TagReferer:
-				return buf.WriteString(c.Get(fiber.HeaderReferer))
-			case TagProtocol:
-				return buf.WriteString(c.Protocol())
-			case TagScheme:
-				return buf.WriteString(c.Scheme())
-			case TagPid:
-				return buf.WriteString(pid)
-			case TagPort:
-				return buf.WriteString(c.Port())
-			case TagIP:
-				return buf.WriteString(c.IP())
-			case TagIPs:
-				return buf.WriteString(c.Get(fiber.HeaderXForwardedFor))
-			case TagHost:
-				return buf.WriteString(c.Hostname())
-			case TagPath:
-				return buf.WriteString(c.Path())
-			case TagURL:
-				return buf.WriteString(c.OriginalURL())
-			case TagUA:
-				return buf.WriteString(c.Get(fiber.HeaderUserAgent))
-			case TagLatency:
-				return buf.WriteString(fmt.Sprintf("%7v", stop.Sub(start).Round(time.Millisecond)))
-			case TagBody:
-				return buf.Write(c.Body())
-			case TagBytesReceived:
-				return appendInt(buf, len(c.Request().Body()))
-			case TagBytesSent:
-				return appendInt(buf, len(c.Response().Body()))
-			case TagRoute:
-				return buf.WriteString(c.Route().Path)
-			case TagStatus:
-				if cfg.enableColors {
-					return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset))
-				}
-				return appendInt(buf, c.Response().StatusCode())
-			case TagResBody:
-				return buf.Write(c.Response().Body())
-			case TagReqHeaders:
-				out := make(map[string]string, 0)
-				if err := c.Bind().Header(&out); err != nil {
-					return 0, err
-				}
-
-				reqHeaders := make([]string, 0)
-				for k, v := range out {
-					reqHeaders = append(reqHeaders, k+"="+v)
-				}
-				return buf.Write([]byte(strings.Join(reqHeaders, "&")))
-			case TagQueryStringParams:
-				return buf.WriteString(c.Request().URI().QueryArgs().String())
-			case TagMethod:
-				if cfg.enableColors {
-					return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset))
-				}
-				return buf.WriteString(c.Method())
-			case TagBlack:
-				return buf.WriteString(colors.Black)
-			case TagRed:
-				return buf.WriteString(colors.Red)
-			case TagGreen:
-				return buf.WriteString(colors.Green)
-			case TagYellow:
-				return buf.WriteString(colors.Yellow)
-			case TagBlue:
-				return buf.WriteString(colors.Blue)
-			case TagMagenta:
-				return buf.WriteString(colors.Magenta)
-			case TagCyan:
-				return buf.WriteString(colors.Cyan)
-			case TagWhite:
-				return buf.WriteString(colors.White)
-			case TagReset:
-				return buf.WriteString(colors.Reset)
-			case TagError:
-				if chainErr != nil {
-					return buf.WriteString(chainErr.Error())
-				}
-				return buf.WriteString("-")
-			default:
-				// Check if we have a value tag i.e.: "reqHeader:x-key"
-				switch {
-				case strings.HasPrefix(tag, TagReqHeader):
-					return buf.WriteString(c.Get(tag[10:]))
-				case strings.HasPrefix(tag, TagRespHeader):
-					return buf.WriteString(c.GetRespHeader(tag[11:]))
-				case strings.HasPrefix(tag, TagQuery):
-					return buf.WriteString(c.Query(tag[6:]))
-				case strings.HasPrefix(tag, TagForm):
-					return buf.WriteString(c.FormValue(tag[5:]))
-				case strings.HasPrefix(tag, TagCookie):
-					return buf.WriteString(c.Cookies(tag[7:]))
-				case strings.HasPrefix(tag, TagLocals):
-					switch v := c.Locals(tag[7:]).(type) {
-					case []byte:
-						return buf.Write(v)
-					case string:
-						return buf.WriteString(v)
-					case nil:
-						return 0, nil
-					default:
-						return buf.WriteString(fmt.Sprintf("%v", v))
-					}
-				}
-			}
-			return 0, nil
-		})
-		// Also write errors to the buffer
-		if err != nil {
-			_, _ = buf.WriteString(err.Error())
-		}
+		// Logger instance & update some logger data fields
 		mu.Lock()
-		// Write buffer to output
-		if _, err := cfg.Output.Write(buf.Bytes()); err != nil {
-			// Write error to output
-			if _, err := cfg.Output.Write([]byte(err.Error())); err != nil {
-				// There is something wrong with the given io.Writer
-				fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
-			}
+		data.ChainErr = chainErr
+		data.Start = start
+		data.Stop = stop
+
+		if err = cfg.LoggerFunc(c, data, cfg); err != nil {
+			return err
 		}
 		mu.Unlock()
-		// Put buffer back to pool
-		bytebufferpool.Put(buf)
 
 		return nil
 	}
 }
-
-func appendInt(buf *bytebufferpool.ByteBuffer, v int) (int, error) {
-	old := len(buf.B)
-	buf.B = fasthttp.AppendUint(buf.B, v)
-	return len(buf.B) - old, nil
-}
diff --git a/path.go b/path.go
index 9874cf70..c6ff72da 100644
--- a/path.go
+++ b/path.go
@@ -60,7 +60,7 @@ const (
 	paramConstraintDataSeparator byte = ','  // separator of datas of type constraint for a parameter
 )
 
-// parameter constraint types
+// TypeConstraint parameter constraint types
 type TypeConstraint int16
 
 type Constraint struct {
@@ -79,7 +79,7 @@ const (
 	guidConstraint
 	minLenConstraint
 	maxLenConstraint
-	exactLenConstraint
+	lenConstraint
 	betweenLenConstraint
 	minConstraint
 	maxConstraint
@@ -249,7 +249,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r
 	// find constraint part if exists in the parameter part and remove it
 	if parameterEndPosition > 0 {
 		parameterConstraintStart = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition], parameterConstraintStartChars)
-		parameterConstraintEnd = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition+1], parameterConstraintEndChars)
+		parameterConstraintEnd = findLastCharsetPosition(pattern[0:parameterEndPosition+1], parameterConstraintEndChars)
 	}
 
 	// cut params part
@@ -259,20 +259,30 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r
 	// Check has constraint
 	var constraints []*Constraint
 
-	if hasConstraint := (parameterConstraintStart != -1 && parameterConstraintEnd != -1); hasConstraint {
+	if hasConstraint := parameterConstraintStart != -1 && parameterConstraintEnd != -1; hasConstraint {
 		constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd]
-		userconstraints := strings.Split(constraintString, string(parameterConstraintSeparatorChars))
-		constraints = make([]*Constraint, 0, len(userconstraints))
+		userConstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars))
+		constraints = make([]*Constraint, 0, len(userConstraints))
 
-		for _, c := range userconstraints {
+		for _, c := range userConstraints {
 			start := findNextNonEscapedCharsetPosition(c, parameterConstraintDataStartChars)
-			end := findNextNonEscapedCharsetPosition(c, parameterConstraintDataEndChars)
+			end := findLastCharsetPosition(c, parameterConstraintDataEndChars)
 
 			// Assign constraint
 			if start != -1 && end != -1 {
 				constraint := &Constraint{
 					ID:   getParamConstraintType(c[:start]),
-					Data: strings.Split(RemoveEscapeChar(c[start+1:end]), string(parameterConstraintDataSeparatorChars)),
+					Data: splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)),
+				}
+
+				// remove escapes from data
+				if constraint.ID != regexConstraint {
+					if len(constraint.Data) == 1 {
+						constraint.Data[0] = RemoveEscapeChar(constraint.Data[0])
+					} else if len(constraint.Data) == 2 {
+						constraint.Data[0] = RemoveEscapeChar(constraint.Data[0])
+						constraint.Data[1] = RemoveEscapeChar(constraint.Data[1])
+					}
 				}
 
 				// Precompile regex if has regex constraint
@@ -337,6 +347,18 @@ func findNextCharsetPosition(search string, charset []byte) int {
 	return nextPosition
 }
 
+// findNextCharsetPosition search the last char position from the charset
+func findLastCharsetPosition(search string, charset []byte) int {
+	lastPosition := -1
+	for _, char := range charset {
+		if pos := strings.LastIndexByte(search, char); pos != -1 && (pos < lastPosition || lastPosition == -1) {
+			lastPosition = pos
+		}
+	}
+
+	return lastPosition
+}
+
 // findNextCharsetPositionConstraint search the next char position from the charset
 // unlike findNextCharsetPosition, it takes care of constraint start-end chars to parse route pattern
 func findNextCharsetPositionConstraint(search string, charset []byte) int {
@@ -354,7 +376,7 @@ func findNextCharsetPositionConstraint(search string, charset []byte) int {
 		if char == paramConstraintEnd {
 			constraintEnd = pos
 		}
-		// fmt.Println(string(char))
+
 		if pos != -1 && (pos < nextPosition || nextPosition == -1) {
 			if pos > constraintStart && pos < constraintEnd {
 				nextPosition = pos
@@ -384,6 +406,21 @@ func findNextNonEscapedCharsetPosition(search string, charset []byte) int {
 	return pos
 }
 
+// splitNonEscaped slices s into all substrings separated by sep and returns a slice of the substrings between those separators
+// This function also takes a care of escape char when splitting.
+func splitNonEscaped(s, sep string) []string {
+	var result []string
+	i := findNextNonEscapedCharsetPosition(s, []byte(sep))
+
+	for i > -1 {
+		result = append(result, s[:i])
+		s = s[i+len(sep):]
+		i = findNextNonEscapedCharsetPosition(s, []byte(sep))
+	}
+
+	return append(result, s)
+}
+
 // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions
 func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool {
 	var i, paramsIterator, partLen int
@@ -526,13 +563,13 @@ func getParamConstraintType(constraintPart string) TypeConstraint {
 		return alphaConstraint
 	case ConstraintGuid:
 		return guidConstraint
-	case ConstraintMinLen:
+	case ConstraintMinLen, ConstraintMinLenLower:
 		return minLenConstraint
-	case ConstraintMaxLen:
+	case ConstraintMaxLen, ConstraintMaxLenLower:
 		return maxLenConstraint
-	case ConstraintExactLen:
-		return exactLenConstraint
-	case ConstraintBetweenLen:
+	case ConstraintLen:
+		return lenConstraint
+	case ConstraintBetweenLen, ConstraintBetweenLenLower:
 		return betweenLenConstraint
 	case ConstraintMin:
 		return minConstraint
@@ -555,7 +592,7 @@ func (c *Constraint) CheckConstraint(param string) bool {
 	var num int
 
 	// check data exists
-	needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, exactLenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint}
+	needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, lenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint}
 	needTwoData := []TypeConstraint{betweenLenConstraint, rangeConstraint}
 
 	for _, data := range needOneData {
@@ -598,7 +635,7 @@ func (c *Constraint) CheckConstraint(param string) bool {
 		if len(param) > data {
 			return false
 		}
-	case exactLenConstraint:
+	case lenConstraint:
 		data, _ := strconv.Atoi(c.Data[0])
 
 		if len(param) != data {
diff --git a/prefork.go b/prefork.go
index b3049abc..71db86b8 100644
--- a/prefork.go
+++ b/prefork.go
@@ -28,7 +28,7 @@ func IsChild() bool {
 }
 
 // prefork manages child processes to make use of the OS REUSEPORT or REUSEADDR feature
-func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) {
+func (app *App) prefork(addr string, tlsConfig *tls.Config, cfg ListenConfig) (err error) {
 	// 👶 child process 👶
 	if IsChild() {
 		// use 1 cpu core per child process
@@ -36,8 +36,8 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error)
 		var ln net.Listener
 		// Linux will use SO_REUSEPORT and Windows falls back to SO_REUSEADDR
 		// Only tcp4 or tcp6 is supported when preforking, both are not supported
-		if ln, err = reuseport.Listen(network, addr); err != nil {
-			if !app.config.DisableStartupMessage {
+		if ln, err = reuseport.Listen(cfg.ListenerNetwork, addr); err != nil {
+			if !cfg.DisableStartupMessage {
 				time.Sleep(100 * time.Millisecond) // avoid colliding with startup message
 			}
 			return fmt.Errorf("prefork: %v", err)
@@ -53,6 +53,10 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error)
 		// prepare the server for the start
 		app.startupProcess()
 
+		if cfg.ListenerAddrFunc != nil {
+			cfg.ListenerAddrFunc(ln.Addr())
+		}
+
 		// listen for incoming connections
 		return app.server.Serve(ln)
 	}
@@ -94,6 +98,7 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error)
 		cmd.Env = append(os.Environ(),
 			fmt.Sprintf("%s=%s", envPreforkChildKey, envPreforkChildVal),
 		)
+
 		if err = cmd.Start(); err != nil {
 			return fmt.Errorf("failed to start a child prefork process, error: %v", err)
 		}
@@ -119,8 +124,13 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error)
 	}
 
 	// Print startup message
-	if !app.config.DisableStartupMessage {
-		app.startupMessage(addr, tlsConfig != nil, ","+strings.Join(pids, ","))
+	if !cfg.DisableStartupMessage {
+		app.startupMessage(addr, tlsConfig != nil, ","+strings.Join(pids, ","), cfg)
+	}
+
+	// Print routes
+	if cfg.EnablePrintRoutes {
+		app.printRoutesMessage()
 	}
 
 	// return error if child crashes
diff --git a/register.go b/register.go
new file mode 100644
index 00000000..9f0bf92d
--- /dev/null
+++ b/register.go
@@ -0,0 +1,128 @@
+// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
+// 🤖 Github Repository: https://github.com/gofiber/fiber
+// 📌 API Documentation: https://docs.gofiber.io
+
+package fiber
+
+// Register defines all router handle interface generate by Route().
+type Register interface {
+	All(handlers ...Handler) Register
+	Get(handlers ...Handler) Register
+	Head(handlers ...Handler) Register
+	Post(handlers ...Handler) Register
+	Put(handlers ...Handler) Register
+	Delete(handlers ...Handler) Register
+	Connect(handlers ...Handler) Register
+	Options(handlers ...Handler) Register
+	Trace(handlers ...Handler) Register
+	Patch(handlers ...Handler) Register
+
+	Add(method string, handlers ...Handler) Register
+
+	Static(root string, config ...Static) Register
+
+	Route(path string) Register
+}
+
+var _ (Register) = (*Registering)(nil)
+
+// Registering struct
+type Registering struct {
+	app *App
+
+	path string
+}
+
+// All registers a middleware route that will match requests
+// with the provided path which is stored in register struct.
+//
+//	app.Route("/").All(func(c fiber.Ctx) error {
+//	     return c.Next()
+//	})
+//	app.Route("/api").All(func(c fiber.Ctx) error {
+//	     return c.Next()
+//	})
+//	app.Route("/api").All(handler, func(c fiber.Ctx) error {
+//	     return c.Next()
+//	})
+//
+// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
+func (r *Registering) All(handlers ...Handler) Register {
+	r.app.register(methodUse, r.path, handlers...)
+	return r
+}
+
+// Get registers a route for GET methods that requests a representation
+// of the specified resource. Requests using GET should only retrieve data.
+func (r *Registering) Get(handlers ...Handler) Register {
+	r.app.Add(MethodHead, r.path, handlers...).Add(MethodGet, r.path, handlers...)
+	return r
+}
+
+// Head registers a route for HEAD methods that asks for a response identical
+// to that of a GET request, but without the response body.
+func (r *Registering) Head(handlers ...Handler) Register {
+	return r.Add(MethodHead, handlers...)
+}
+
+// Post registers a route for POST methods that is used to submit an entity to the
+// specified resource, often causing a change in state or side effects on the server.
+func (r *Registering) Post(handlers ...Handler) Register {
+	return r.Add(MethodPost, handlers...)
+}
+
+// Put registers a route for PUT methods that replaces all current representations
+// of the target resource with the request payload.
+func (r *Registering) Put(handlers ...Handler) Register {
+	return r.Add(MethodPut, handlers...)
+}
+
+// Delete registers a route for DELETE methods that deletes the specified resource.
+func (r *Registering) Delete(handlers ...Handler) Register {
+	return r.Add(MethodDelete, handlers...)
+}
+
+// Connect registers a route for CONNECT methods that establishes a tunnel to the
+// server identified by the target resource.
+func (r *Registering) Connect(handlers ...Handler) Register {
+	return r.Add(MethodConnect, handlers...)
+}
+
+// Options registers a route for OPTIONS methods that is used to describe the
+// communication options for the target resource.
+func (r *Registering) Options(handlers ...Handler) Register {
+	return r.Add(MethodOptions, handlers...)
+}
+
+// Trace registers a route for TRACE methods that performs a message loop-back
+// test along the r.Path to the target resource.
+func (r *Registering) Trace(handlers ...Handler) Register {
+	return r.Add(MethodTrace, handlers...)
+}
+
+// Patch registers a route for PATCH methods that is used to apply partial
+// modifications to a resource.
+func (r *Registering) Patch(handlers ...Handler) Register {
+	return r.Add(MethodPatch, handlers...)
+}
+
+// Add allows you to specify a HTTP method to register a route
+func (r *Registering) Add(method string, handlers ...Handler) Register {
+	r.app.register(method, r.path, handlers...)
+	return r
+}
+
+// Static will create a file server serving static files
+func (r *Registering) Static(root string, config ...Static) Register {
+	r.app.registerStatic(r.path, root, config...)
+	return r
+}
+
+// Route returns a new Register instance whose route path takes
+// the path in the current instance as its prefix.
+func (r *Registering) Route(path string) Register {
+	// Create new group
+	route := &Registering{app: r.app, path: getGroupPath(r.path, path)}
+
+	return route
+}
diff --git a/router.go b/router.go
index 559635b3..614ff937 100644
--- a/router.go
+++ b/router.go
@@ -36,7 +36,7 @@ type Router interface {
 
 	Group(prefix string, handlers ...Handler) Router
 
-	Route(prefix string, fn func(router Router), name ...string) Router
+	Route(path string) Register
 
 	Mount(prefix string, fiber *App) Router
 


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ffd8b927..1ffb5410 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -35,4 +35,8 @@ jobs:
     - name: Fetch Repository
       uses: actions/checkout@v3
     - name: Run Test
-      run: go test ./... -v -race
+      uses: nick-fields/retry@v2
+      with:
+        max_attempts: 3
+        timeout_minutes: 15
+        command: go test ./... -v -race
diff --git a/app_test.go b/app_test.go
index 5a3dc314..b6a32605 100644
--- a/app_test.go
+++ b/app_test.go
@@ -83,12 +83,14 @@ func Test_App_MethodNotAllowed(t *testing.T) {
 	resp, err = app.Test(httptest.NewRequest(MethodTrace, "/", nil))
 	require.NoError(t, err)
 	require.Equal(t, 405, resp.StatusCode)
-	require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow))
+	require.Equal(t, "GET, POST, OPTIONS", resp.Header.Get(HeaderAllow))
 
 	resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil))
 	require.NoError(t, err)
 	require.Equal(t, 405, resp.StatusCode)
-	require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow))
+	require.Equal(t, "GET, POST, OPTIONS", resp.Header.Get(HeaderAllow))
+
+	app.Head("/", testEmptyHandler)
 
 	resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil))
 	require.NoError(t, err)
@@ -313,7 +315,7 @@ func Test_App_Mount(t *testing.T) {
 	resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil))
 	require.NoError(t, err, "app.Test(req)")
 	require.Equal(t, 200, resp.StatusCode, "Status code")
-	require.Equal(t, uint32(2), app.handlersCount)
+	require.Equal(t, uint32(1), app.handlersCount)
 }
 
 func Test_App_Use_Params(t *testing.T) {
@@ -597,16 +599,14 @@ func Test_App_New(t *testing.T) {
 
 func Test_App_Config(t *testing.T) {
 	app := New(Config{
-		DisableStartupMessage: true,
+		StrictRouting: true,
 	})
-	require.True(t, app.Config().DisableStartupMessage)
+	require.True(t, app.Config().StrictRouting)
 }
 
 func Test_App_Shutdown(t *testing.T) {
 	t.Run("success", func(t *testing.T) {
-		app := New(Config{
-			DisableStartupMessage: true,
-		})
+		app := New()
 		require.True(t, app.Shutdown() == nil)
 	})
 
@@ -962,7 +962,7 @@ func Test_App_Group_Mount(t *testing.T) {
 	resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
 	require.NoError(t, err, "app.Test(req)")
 	require.Equal(t, 200, resp.StatusCode, "Status code")
-	require.Equal(t, uint32(2), app.handlersCount)
+	require.Equal(t, uint32(1), app.handlersCount)
 }
 
 func Test_App_Group(t *testing.T) {
@@ -1030,48 +1030,46 @@ func Test_App_Route(t *testing.T) {
 
 	app := New()
 
-	grp := app.Route("/test", func(grp Router) {
-		grp.Get("/", dummyHandler)
-		grp.Get("/:demo?", dummyHandler)
-		grp.Connect("/CONNECT", dummyHandler)
-		grp.Put("/PUT", dummyHandler)
-		grp.Post("/POST", dummyHandler)
-		grp.Delete("/DELETE", dummyHandler)
-		grp.Head("/HEAD", dummyHandler)
-		grp.Patch("/PATCH", dummyHandler)
-		grp.Options("/OPTIONS", dummyHandler)
-		grp.Trace("/TRACE", dummyHandler)
-		grp.All("/ALL", dummyHandler)
-		grp.Use(dummyHandler)
-		grp.Use("/USE", dummyHandler)
-	})
+	register := app.Route("/test").
+		Get(dummyHandler).
+		Post(dummyHandler).
+		Put(dummyHandler).
+		Delete(dummyHandler).
+		Connect(dummyHandler).
+		Options(dummyHandler).
+		Trace(dummyHandler).
+		Patch(dummyHandler)
 
 	testStatus200(t, app, "/test", MethodGet)
-	testStatus200(t, app, "/test/john", MethodGet)
-	testStatus200(t, app, "/test/CONNECT", MethodConnect)
-	testStatus200(t, app, "/test/PUT", MethodPut)
-	testStatus200(t, app, "/test/POST", MethodPost)
-	testStatus200(t, app, "/test/DELETE", MethodDelete)
-	testStatus200(t, app, "/test/HEAD", MethodHead)
-	testStatus200(t, app, "/test/PATCH", MethodPatch)
-	testStatus200(t, app, "/test/OPTIONS", MethodOptions)
-	testStatus200(t, app, "/test/TRACE", MethodTrace)
-	testStatus200(t, app, "/test/ALL", MethodPost)
-	testStatus200(t, app, "/test/oke", MethodGet)
-	testStatus200(t, app, "/test/USE/oke", MethodGet)
+	testStatus200(t, app, "/test", MethodHead)
+	testStatus200(t, app, "/test", MethodPost)
+	testStatus200(t, app, "/test", MethodPut)
+	testStatus200(t, app, "/test", MethodDelete)
+	testStatus200(t, app, "/test", MethodConnect)
+	testStatus200(t, app, "/test", MethodOptions)
+	testStatus200(t, app, "/test", MethodTrace)
+	testStatus200(t, app, "/test", MethodPatch)
+
+	register.Route("/v1").Get(dummyHandler).Post(dummyHandler)
+
+	resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1", nil))
+	require.NoError(t, err, "app.Test(req)")
+	require.Equal(t, 200, resp.StatusCode, "Status code")
 
-	grp.Route("/v1", func(grp Router) {
-		grp.Post("/", dummyHandler)
-		grp.Get("/users", dummyHandler)
-	})
+	resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1", nil))
+	require.NoError(t, err, "app.Test(req)")
+	require.Equal(t, 200, resp.StatusCode, "Status code")
 
-	resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1/", nil))
+	register.Route("/v1").Route("/v2").Route("/v3").Get(dummyHandler).Trace(dummyHandler)
+
+	resp, err = app.Test(httptest.NewRequest(MethodTrace, "/test/v1/v2/v3", nil))
 	require.NoError(t, err, "app.Test(req)")
 	require.Equal(t, 200, resp.StatusCode, "Status code")
 
-	resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/UsErS", nil))
+	resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/v2/v3", nil))
 	require.NoError(t, err, "app.Test(req)")
 	require.Equal(t, 200, resp.StatusCode, "Status code")
+
 }
 
 func Test_App_Deep_Group(t *testing.T) {
@@ -1096,7 +1094,6 @@ func Test_App_Deep_Group(t *testing.T) {
 // go test -run Test_App_Next_Method
 func Test_App_Next_Method(t *testing.T) {
 	app := New()
-	app.config.DisableStartupMessage = true
 
 	app.Use(func(c Ctx) error {
 		require.Equal(t, MethodGet, c.Method())
@@ -1138,7 +1135,6 @@ func Test_NewError(t *testing.T) {
 // go test -run Test_Test_Timeout
 func Test_Test_Timeout(t *testing.T) {
 	app := New()
-	app.config.DisableStartupMessage = true
 
 	app.Get("/", testEmptyHandler)
 
@@ -1164,7 +1160,6 @@ func (errorReader) Read([]byte) (int, error) {
 // go test -run Test_Test_DumpError
 func Test_Test_DumpError(t *testing.T) {
 	app := New()
-	app.config.DisableStartupMessage = true
 
 	app.Get("/", testEmptyHandler)
 
@@ -1209,7 +1204,7 @@ func Test_App_Stack(t *testing.T) {
 	stack := app.Stack()
 	require.Equal(t, 9, len(stack))
 	require.Equal(t, 3, len(stack[methodInt(MethodGet)]))
-	require.Equal(t, 3, len(stack[methodInt(MethodHead)]))
+	require.Equal(t, 1, len(stack[methodInt(MethodHead)]))
 	require.Equal(t, 2, len(stack[methodInt(MethodPost)]))
 	require.Equal(t, 1, len(stack[methodInt(MethodPut)]))
 	require.Equal(t, 1, len(stack[methodInt(MethodPatch)]))
@@ -1228,16 +1223,15 @@ func Test_App_HandlersCount(t *testing.T) {
 	app.Post("/path3", testEmptyHandler)
 
 	count := app.HandlersCount()
-	require.Equal(t, uint32(4), count)
+	require.Equal(t, uint32(3), count)
 }
 
 // go test -run Test_App_ReadTimeout
 func Test_App_ReadTimeout(t *testing.T) {
 	app := New(Config{
-		ReadTimeout:           time.Nanosecond,
-		IdleTimeout:           time.Minute,
-		DisableStartupMessage: true,
-		DisableKeepalive:      true,
+		ReadTimeout:      time.Nanosecond,
+		IdleTimeout:      time.Minute,
+		DisableKeepalive: true,
 	})
 
 	app.Get("/read-timeout", func(c Ctx) error {
@@ -1264,14 +1258,12 @@ func Test_App_ReadTimeout(t *testing.T) {
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.Listen(":4004"))
+	require.Nil(t, app.Listen(":4004", ListenConfig{DisableStartupMessage: true}))
 }
 
 // go test -run Test_App_BadRequest
 func Test_App_BadRequest(t *testing.T) {
-	app := New(Config{
-		DisableStartupMessage: true,
-	})
+	app := New()
 
 	app.Get("/bad-request", func(c Ctx) error {
 		return c.SendString("I should not be sent")
@@ -1296,14 +1288,13 @@ func Test_App_BadRequest(t *testing.T) {
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.Listen(":4005"))
+	require.Nil(t, app.Listen(":4005", ListenConfig{DisableStartupMessage: true}))
 }
 
 // go test -run Test_App_SmallReadBuffer
 func Test_App_SmallReadBuffer(t *testing.T) {
 	app := New(Config{
-		ReadBufferSize:        1,
-		DisableStartupMessage: true,
+		ReadBufferSize: 1,
 	})
 
 	app.Get("/small-read-buffer", func(c Ctx) error {
@@ -1320,7 +1311,7 @@ func Test_App_SmallReadBuffer(t *testing.T) {
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.Listen(":4006"))
+	require.Nil(t, app.Listen(":4006", ListenConfig{DisableStartupMessage: true}))
 }
 
 func Test_App_Server(t *testing.T) {
diff --git a/client_test.go b/client_test.go
index 987b0c3c..e3d78dca 100644
--- a/client_test.go
+++ b/client_test.go
@@ -28,13 +28,17 @@ func Test_Client_Invalid_URL(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
-		return c.SendString(c.Hostname())
+		return c.SendString(c.Host())
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	a := Get("http://example.com\r\n\r\nGET /\r\n\r\n")
 
@@ -66,13 +70,17 @@ func Test_Client_Get(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
-		return c.SendString(c.Hostname())
+		return c.SendString(c.Host())
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	for i := 0; i < 5; i++ {
 		a := Get("http://example.com")
@@ -92,14 +100,17 @@ func Test_Client_Head(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
-	app.Get("/", func(c Ctx) error {
-		return c.SendString(c.Hostname())
+	app.Head("/", func(c Ctx) error {
+		return c.SendStatus(StatusAccepted)
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
-
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 	for i := 0; i < 5; i++ {
 		a := Head("http://example.com")
 
@@ -107,7 +118,7 @@ func Test_Client_Head(t *testing.T) {
 
 		code, body, errs := a.String()
 
-		require.Equal(t, StatusOK, code)
+		require.Equal(t, StatusAccepted, code)
 		require.Equal(t, "", body)
 		require.Equal(t, 0, len(errs))
 	}
@@ -118,14 +129,18 @@ func Test_Client_Post(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Post("/", func(c Ctx) error {
 		return c.Status(StatusCreated).
 			SendString(c.FormValue("foo"))
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	for i := 0; i < 5; i++ {
 		args := AcquireArgs()
@@ -152,13 +167,17 @@ func Test_Client_Put(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Put("/", func(c Ctx) error {
 		return c.SendString(c.FormValue("foo"))
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	for i := 0; i < 5; i++ {
 		args := AcquireArgs()
@@ -185,13 +204,17 @@ func Test_Client_Patch(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Patch("/", func(c Ctx) error {
 		return c.SendString(c.FormValue("foo"))
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	for i := 0; i < 5; i++ {
 		args := AcquireArgs()
@@ -218,14 +241,18 @@ func Test_Client_Delete(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Delete("/", func(c Ctx) error {
 		return c.Status(StatusNoContent).
 			SendString("deleted")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	for i := 0; i < 5; i++ {
 		args := AcquireArgs()
@@ -249,13 +276,17 @@ func Test_Client_UserAgent(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		return c.Send(c.Request().Header.UserAgent())
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	t.Run("default", func(t *testing.T) {
 		for i := 0; i < 5; i++ {
@@ -391,13 +422,17 @@ func Test_Client_Agent_Host(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
-		return c.SendString(c.Hostname())
+		return c.SendString(c.Host())
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	a := Get("http://1.1.1.1:8080").
 		Host("example.com").
@@ -487,13 +522,17 @@ func Test_Client_Agent_Custom_Response(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		return c.SendString("custom")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	for i := 0; i < 5; i++ {
 		a := AcquireAgent()
@@ -524,13 +563,17 @@ func Test_Client_Agent_Dest(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		return c.SendString("dest")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	t.Run("small dest", func(t *testing.T) {
 		dest := []byte("de")
@@ -592,9 +635,13 @@ func Test_Client_Agent_RetryIf(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	a := Post("http://example.com").
 		RetryIf(func(req *Request) bool {
@@ -699,7 +746,7 @@ func Test_Client_Agent_MultipartForm(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Post("/", func(c Ctx) error {
 		require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType))
@@ -711,7 +758,11 @@ func Test_Client_Agent_MultipartForm(t *testing.T) {
 		return c.Send(c.Request().Body())
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	args := AcquireArgs()
 
@@ -754,7 +805,7 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Post("/", func(c Ctx) error {
 		require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType))
@@ -781,7 +832,11 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) {
 		return c.SendString("multipart form files")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	for i := 0; i < 5; i++ {
 		ff := AcquireFormFile()
@@ -885,14 +940,18 @@ func Test_Client_Agent_Timeout(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		time.Sleep(time.Millisecond * 200)
 		return c.SendString("timeout")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	a := Get("http://example.com").
 		Timeout(time.Millisecond * 50)
@@ -911,13 +970,17 @@ func Test_Client_Agent_Reuse(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		return c.SendString("reuse")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	a := Get("http://example.com").
 		Reuse()
@@ -952,13 +1015,17 @@ func Test_Client_Agent_InsecureSkipVerify(t *testing.T) {
 
 	ln = tls.NewListener(ln, serverTLSConf)
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		return c.SendString("ignore tls")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	code, body, errs := Get("https://" + ln.Addr().String()).
 		InsecureSkipVerify().
@@ -981,13 +1048,17 @@ func Test_Client_Agent_TLS(t *testing.T) {
 
 	ln = tls.NewListener(ln, serverTLSConf)
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		return c.SendString("tls")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	code, body, errs := Get("https://" + ln.Addr().String()).
 		TLSConfig(clientTLSConf).
@@ -1003,7 +1074,7 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		if c.Request().URI().QueryArgs().Has("foo") {
@@ -1015,7 +1086,11 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) {
 		return c.SendString("redirect")
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	t.Run("success", func(t *testing.T) {
 		a := Get("http://example.com?foo").
@@ -1049,7 +1124,7 @@ func Test_Client_Agent_Struct(t *testing.T) {
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", func(c Ctx) error {
 		return c.JSON(data{true})
@@ -1059,7 +1134,11 @@ func Test_Client_Agent_Struct(t *testing.T) {
 		return c.SendString(`{"success"`)
 	})
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	t.Run("success", func(t *testing.T) {
 		t.Parallel()
@@ -1128,11 +1207,15 @@ func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), exce
 
 	ln := fasthttputil.NewInmemoryListener()
 
-	app := New(Config{DisableStartupMessage: true})
+	app := New()
 
 	app.Get("/", handler)
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, nil, app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	c := 1
 	if len(count) > 0 {
diff --git a/ctx_test.go b/ctx_test.go
index 9998b474..82e21d39 100644
--- a/ctx_test.go
+++ b/ctx_test.go
@@ -772,18 +772,18 @@ func Test_Ctx_Get(t *testing.T) {
 	require.Equal(t, "default", c.Get("unknown", "default"))
 }
 
-// go test -run Test_Ctx_Hostname
-func Test_Ctx_Hostname(t *testing.T) {
+// go test -run Test_Ctx_Host
+func Test_Ctx_Host(t *testing.T) {
 	t.Parallel()
 	app := New()
 	c := app.NewCtx(&fasthttp.RequestCtx{})
 
 	c.Request().SetRequestURI("http://google.com/test")
-	require.Equal(t, "google.com", c.Hostname())
+	require.Equal(t, "google.com", c.Host())
 }
 
-// go test -run Test_Ctx_Hostname_Untrusted
-func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) {
+// go test -run Test_Ctx_Host_UntrustedProxy
+func Test_Ctx_Host_UntrustedProxy(t *testing.T) {
 	t.Parallel()
 	// Don't trust any proxy
 	{
@@ -791,7 +791,7 @@ func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) {
 		c := app.NewCtx(&fasthttp.RequestCtx{})
 		c.Request().SetRequestURI("http://google.com/test")
 		c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
-		require.Equal(t, "google.com", c.Hostname())
+		require.Equal(t, "google.com", c.Host())
 		app.ReleaseCtx(c)
 	}
 	// Trust to specific proxy list
@@ -800,48 +800,89 @@ func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) {
 		c := app.NewCtx(&fasthttp.RequestCtx{})
 		c.Request().SetRequestURI("http://google.com/test")
 		c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
-		require.Equal(t, "google.com", c.Hostname())
+		require.Equal(t, "google.com", c.Host())
 		app.ReleaseCtx(c)
 	}
 }
 
-// go test -run Test_Ctx_Hostname_Trusted
-func Test_Ctx_Hostname_TrustedProxy(t *testing.T) {
+// go test -run Test_Ctx_Host_TrustedProxy
+func Test_Ctx_Host_TrustedProxy(t *testing.T) {
 	t.Parallel()
 	{
 		app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}})
 		c := app.NewCtx(&fasthttp.RequestCtx{})
 		c.Request().SetRequestURI("http://google.com/test")
 		c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
-		require.Equal(t, "google1.com", c.Hostname())
+		require.Equal(t, "google1.com", c.Host())
 		app.ReleaseCtx(c)
 	}
 }
 
-// go test -run Test_Ctx_Hostname_UntrustedProxyRange
-func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) {
+// go test -run Test_Ctx_Host_TrustedProxyRange
+func Test_Ctx_Host_TrustedProxyRange(t *testing.T) {
 	t.Parallel()
 
 	app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}})
 	c := app.NewCtx(&fasthttp.RequestCtx{})
 	c.Request().SetRequestURI("http://google.com/test")
 	c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
-	require.Equal(t, "google1.com", c.Hostname())
+	require.Equal(t, "google1.com", c.Host())
 	app.ReleaseCtx(c)
 }
 
-// go test -run Test_Ctx_Hostname_UntrustedProxyRange
-func Test_Ctx_Hostname_UntrustedProxyRange(t *testing.T) {
+// go test -run Test_Ctx_Host_UntrustedProxyRange
+func Test_Ctx_Host_UntrustedProxyRange(t *testing.T) {
 	t.Parallel()
 
 	app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.0.0.0/30"}})
 	c := app.NewCtx(&fasthttp.RequestCtx{})
 	c.Request().SetRequestURI("http://google.com/test")
 	c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
-	require.Equal(t, "google.com", c.Hostname())
+	require.Equal(t, "google.com", c.Host())
 	app.ReleaseCtx(c)
 }
 
+// go test -v -run=^$ -bench=Benchmark_Ctx_Host -benchmem -count=4
+func Benchmark_Ctx_Host(b *testing.B) {
+	app := New()
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+	c.Request().SetRequestURI("http://google.com/test")
+	var host string
+	b.ReportAllocs()
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		host = c.Host()
+	}
+	require.Equal(b, "google.com", host)
+}
+
+// go test -run Test_Ctx_Hostname
+func Test_Ctx_Hostname(t *testing.T) {
+	t.Parallel()
+	app := New()
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+
+	c.Request().SetRequestURI("http://google.com/test")
+	require.Equal(t, "google.com", c.Hostname())
+
+	c.Request().SetRequestURI("http://google.com:8080/test")
+	require.Equal(t, "google.com", c.Hostname())
+}
+
+// go test -v -run=^$ -bench=Benchmark_Ctx_Hostname -benchmem -count=4
+func Benchmark_Ctx_Hostname(b *testing.B) {
+	app := New()
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+	c.Request().SetRequestURI("http://google.com:8080/test")
+	var hostname string
+	b.ReportAllocs()
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		hostname = c.Hostname()
+	}
+	require.Equal(b, "google.com", hostname)
+}
+
 // go test -run Test_Ctx_Port
 func Test_Ctx_Port(t *testing.T) {
 	t.Parallel()
@@ -872,19 +913,81 @@ func Test_Ctx_PortInHandler(t *testing.T) {
 // go test -run Test_Ctx_IP
 func Test_Ctx_IP(t *testing.T) {
 	t.Parallel()
+
 	app := New()
 	c := app.NewCtx(&fasthttp.RequestCtx{})
 
+	// default behaviour will return the remote IP from the stack
+	require.Equal(t, "0.0.0.0", c.IP())
+
+	// X-Forwarded-For is set, but it is ignored because proxyHeader is not set
+	c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1")
 	require.Equal(t, "0.0.0.0", c.IP())
 }
 
 // go test -run Test_Ctx_IP_ProxyHeader
 func Test_Ctx_IP_ProxyHeader(t *testing.T) {
 	t.Parallel()
-	app := New(Config{ProxyHeader: "Real-Ip"})
-	c := app.NewCtx(&fasthttp.RequestCtx{})
 
-	require.Equal(t, "", c.IP())
+	// make sure that the same behaviour exists for different proxy header names
+	proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor}
+
+	for _, proxyHeaderName := range proxyHeaderNames {
+		app := New(Config{ProxyHeader: proxyHeaderName})
+		c := app.NewCtx(&fasthttp.RequestCtx{})
+
+		c.Request().Header.Set(proxyHeaderName, "0.0.0.1")
+		require.Equal(t, "0.0.0.1", c.IP())
+
+		// without IP validation we return the full string
+		c.Request().Header.Set(proxyHeaderName, "0.0.0.1, 0.0.0.2")
+		require.Equal(t, "0.0.0.1, 0.0.0.2", c.IP())
+
+		// without IP validation we return invalid IPs
+		c.Request().Header.Set(proxyHeaderName, "invalid, 0.0.0.2, 0.0.0.3")
+		require.Equal(t, "invalid, 0.0.0.2, 0.0.0.3", c.IP())
+
+		// when proxy header is enabled but the value is empty, without IP validation we return an empty string
+		c.Request().Header.Set(proxyHeaderName, "")
+		require.Equal(t, "", c.IP())
+
+		// without IP validation we return an invalid IP
+		c.Request().Header.Set(proxyHeaderName, "not-valid-ip")
+		require.Equal(t, "not-valid-ip", c.IP())
+	}
+}
+
+// go test -run Test_Ctx_IP_ProxyHeader
+func Test_Ctx_IP_ProxyHeader_With_IP_Validation(t *testing.T) {
+	t.Parallel()
+
+	// make sure that the same behaviour exists for different proxy header names
+	proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor}
+
+	for _, proxyHeaderName := range proxyHeaderNames {
+		app := New(Config{EnableIPValidation: true, ProxyHeader: proxyHeaderName})
+		c := app.NewCtx(&fasthttp.RequestCtx{})
+
+		// when proxy header & validation is enabled and the value is a valid IP, we return it
+		c.Request().Header.Set(proxyHeaderName, "0.0.0.1")
+		require.Equal(t, "0.0.0.1", c.IP())
+
+		// when proxy header & validation is enabled and the value is a list of IPs, we return the first valid IP
+		c.Request().Header.Set(proxyHeaderName, "0.0.0.1, 0.0.0.2")
+		require.Equal(t, "0.0.0.1", c.IP())
+
+		c.Request().Header.Set(proxyHeaderName, "invalid, 0.0.0.2, 0.0.0.3")
+		require.Equal(t, "0.0.0.2", c.IP())
+
+		// when proxy header & validation is enabled but the value is empty, we will ignore the header
+		c.Request().Header.Set(proxyHeaderName, "")
+		require.Equal(t, "0.0.0.0", c.IP())
+
+		// when proxy header & validation is enabled but the value is not an IP, we will ignore the header
+		// and return the IP of the caller
+		c.Request().Header.Set(proxyHeaderName, "not-valid-ip")
+		require.Equal(t, "0.0.0.0", c.IP())
+	}
 }
 
 // go test -run Test_Ctx_IP_UntrustedProxy
@@ -893,7 +996,6 @@ func Test_Ctx_IP_UntrustedProxy(t *testing.T) {
 	app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}, ProxyHeader: HeaderXForwardedFor})
 	c := app.NewCtx(&fasthttp.RequestCtx{})
 	c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1")
-
 	require.Equal(t, "0.0.0.0", c.IP())
 }
 
@@ -903,7 +1005,6 @@ func Test_Ctx_IP_TrustedProxy(t *testing.T) {
 	app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}, ProxyHeader: HeaderXForwardedFor})
 	c := app.NewCtx(&fasthttp.RequestCtx{})
 	c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1")
-
 	require.Equal(t, "0.0.0.1", c.IP())
 }
 
@@ -913,29 +1014,129 @@ func Test_Ctx_IPs(t *testing.T) {
 	app := New()
 	c := app.NewCtx(&fasthttp.RequestCtx{})
 
+	// normal happy path test case
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3")
+	require.Equal(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs())
+
+	// inconsistent space formatting
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1,127.0.0.2  ,127.0.0.3")
+	require.Equal(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs())
+
+	// invalid IPs are allowed to be returned
+	c.Request().Header.Set(HeaderXForwardedFor, "invalid, 127.0.0.1, 127.0.0.2")
+	require.Equal(t, []string{"invalid", "127.0.0.1", "127.0.0.2"}, c.IPs())
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.2")
+	require.Equal(t, []string{"127.0.0.1", "invalid", "127.0.0.2"}, c.IPs())
+
+	// ensure that the ordering of IPs in the header is maintained
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2")
+	require.Equal(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs())
+
+	// empty header
+	c.Request().Header.Set(HeaderXForwardedFor, "")
+	require.Equal(t, 0, len(c.IPs()))
+
+	// missing header
+	c.Request()
+	require.Equal(t, 0, len(c.IPs()))
+}
+
+func Test_Ctx_IPs_With_IP_Validation(t *testing.T) {
+	t.Parallel()
+	app := New(Config{EnableIPValidation: true})
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+
+	// normal happy path test case
 	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3")
 	require.Equal(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs())
 
+	// inconsistent space formatting
 	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1,127.0.0.2  ,127.0.0.3")
 	require.Equal(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs())
 
+	// invalid IPs are in the header
+	c.Request().Header.Set(HeaderXForwardedFor, "invalid, 127.0.0.1, 127.0.0.2")
+	require.Equal(t, []string{"127.0.0.1", "127.0.0.2"}, c.IPs())
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.2")
+	require.Equal(t, []string{"127.0.0.1", "127.0.0.2"}, c.IPs())
+
+	// ensure that the ordering of IPs in the header is maintained
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2")
+	require.Equal(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs())
+
+	// empty header
 	c.Request().Header.Set(HeaderXForwardedFor, "")
 	require.Equal(t, 0, len(c.IPs()))
+
+	// missing header
+	c.Request()
+	require.Equal(t, 0, len(c.IPs()))
 }
 
 // go test -v -run=^$ -bench=Benchmark_Ctx_IPs -benchmem -count=4
 func Benchmark_Ctx_IPs(b *testing.B) {
 	app := New()
 	c := app.NewCtx(&fasthttp.RequestCtx{})
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1")
+	var res []string
+	b.ReportAllocs()
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		res = c.IPs()
+	}
+	require.Equal(b, []string{"127.0.0.1", "invalid", "127.0.0.1"}, res)
+}
 
-	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.1, 127.0.0.1")
+func Benchmark_Ctx_IPs_With_IP_Validation(b *testing.B) {
+	app := New(Config{EnableIPValidation: true})
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1")
 	var res []string
 	b.ReportAllocs()
 	b.ResetTimer()
 	for n := 0; n < b.N; n++ {
 		res = c.IPs()
 	}
-	require.Equal(b, []string{"127.0.0.1", "127.0.0.1", "127.0.0.1"}, res)
+	require.Equal(b, []string{"127.0.0.1", "127.0.0.1"}, res)
+}
+
+func Benchmark_Ctx_IP_With_ProxyHeader(b *testing.B) {
+	app := New(Config{ProxyHeader: HeaderXForwardedFor})
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1")
+	var res string
+	b.ReportAllocs()
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		res = c.IP()
+	}
+	require.Equal(b, "127.0.0.1", res)
+}
+
+func Benchmark_Ctx_IP_With_ProxyHeader_and_IP_Validation(b *testing.B) {
+	app := New(Config{ProxyHeader: HeaderXForwardedFor, EnableIPValidation: true})
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+	c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1")
+	var res string
+	b.ReportAllocs()
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		res = c.IP()
+	}
+	require.Equal(b, "127.0.0.1", res)
+}
+
+func Benchmark_Ctx_IP(b *testing.B) {
+	app := New()
+	c := app.NewCtx(&fasthttp.RequestCtx{})
+	c.Request()
+	var res string
+	b.ReportAllocs()
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		res = c.IP()
+	}
+	require.Equal(b, "0.0.0.0", res)
 }
 
 // go test -run Test_Ctx_Is
@@ -1060,7 +1261,7 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) {
 		PSSWithSHA256 = 0x0804
 		VersionTLS13  = 0x0304
 	)
-	app.tlsHandler = &tlsHandler{clientHelloInfo: &tls.ClientHelloInfo{
+	app.tlsHandler = &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{
 		ServerName:        "example.golang",
 		SignatureSchemes:  []tls.SignatureScheme{PSSWithSHA256},
 		SupportedVersions: []uint16{VersionTLS13},
@@ -1185,6 +1386,11 @@ func Test_Ctx_Params(t *testing.T) {
 		require.Equal(t, "", c.Params("optional"))
 		return nil
 	})
+	app.Get("/test5/:id/:Id", func(c Ctx) error {
+		require.Equal(t, "first", c.Params("id"))
+		require.Equal(t, "first", c.Params("Id"))
+		return nil
+	})
 	resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil))
 	require.NoError(t, err, "app.Test(req)")
 	require.Equal(t, StatusOK, resp.StatusCode, "Status code")
@@ -1200,6 +1406,32 @@ func Test_Ctx_Params(t *testing.T) {
 	resp, err = app.Test(httptest.NewRequest(MethodGet, "/test4", nil))
 	require.NoError(t, err, "app.Test(req)")
 	require.Equal(t, StatusOK, resp.StatusCode, "Status code")
+
+	resp, err = app.Test(httptest.NewRequest(MethodGet, "/test5/first/second", nil))
+	require.NoError(t, err, "app.Test(req)")
+	require.Equal(t, StatusOK, resp.StatusCode, "Status code")
+}
+
+func Test_Ctx_Params_Case_Sensitive(t *testing.T) {
+	t.Parallel()
+	app := New(Config{CaseSensitive: true})
+	app.Get("/test/:User", func(c Ctx) error {
+		require.Equal(t, "john", c.Params("User"))
+		require.Equal(t, "", c.Params("user"))
+		return nil
+	})
+	app.Get("/test2/:id/:Id", func(c Ctx) error {
+		require.Equal(t, "first", c.Params("id"))
+		require.Equal(t, "second", c.Params("Id"))
+		return nil
+	})
+	resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil))
+	require.NoError(t, err, "app.Test(req)")
+	require.Equal(t, StatusOK, resp.StatusCode, "Status code")
+
+	resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/first/second", nil))
+	require.Equal(t, nil, err)
+	require.Equal(t, StatusOK, resp.StatusCode, "Status code")
 }
 
 // go test -v -run=^$ -bench=Benchmark_Ctx_Params -benchmem -count=4
@@ -2603,16 +2835,38 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) {
 
 // go test -run Test_Ctx_Get_Location_From_Route_name
 func Test_Ctx_Get_Location_From_Route_name(t *testing.T) {
-	app := New()
-	c := app.NewCtx(&fasthttp.RequestCtx{})
+	t.Run("case insensitive", func(t *testing.T) {
+		app := New()
+		c := app.NewCtx(&fasthttp.RequestCtx{})
+		app.Get("/user/:name", func(c Ctx) error {
+			return c.SendString(c.Params("name"))
+		}).Name("User")
 
-	app.Get("/user/:name", func(c Ctx) error {
-		return c.SendString(c.Params("name"))
-	}).Name("User")
+		location, err := c.GetRouteURL("User", Map{"name": "fiber"})
+		require.NoError(t, err)
+		require.Equal(t, "/user/fiber", location)
 
-	location, err := c.GetRouteURL("User", Map{"name": "fiber"})
-	require.NoError(t, err)
-	require.Equal(t, "/user/fiber", location)
+		location, err = c.GetRouteURL("User", Map{"Name": "fiber"})
+		require.NoError(t, err)
+		require.Equal(t, "/user/fiber", location)
+	})
+
+	t.Run("case sensitive", func(t *testing.T) {
+		app := New(Config{CaseSensitive: true})
+		c := app.NewCtx(&fasthttp.RequestCtx{})
+		defer app.ReleaseCtx(c)
+		app.Get("/user/:name", func(c Ctx) error {
+			return c.SendString(c.Params("name"))
+		}).Name("User")
+
+		location, err := c.GetRouteURL("User", Map{"name": "fiber"})
+		require.NoError(t, err)
+		require.Equal(t, "/user/fiber", location)
+
+		location, err = c.GetRouteURL("User", Map{"Name": "fiber"})
+		require.NoError(t, err)
+		require.Equal(t, "/user/", location)
+	})
 }
 
 // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy
diff --git a/helpers_test.go b/helpers_test.go
index e299cd44..75c4afc5 100644
--- a/helpers_test.go
+++ b/helpers_test.go
@@ -5,9 +5,7 @@
 package fiber
 
 import (
-	"crypto/tls"
 	"fmt"
-	"net"
 	"strings"
 	"testing"
 	"time"
@@ -162,48 +160,6 @@ func Benchmark_Utils_IsNoCache(b *testing.B) {
 	require.True(b, ok)
 }
 
-func Test_Utils_lnMetadata(t *testing.T) {
-	t.Run("closed listen", func(t *testing.T) {
-		ln, err := net.Listen(NetworkTCP, ":0")
-		require.NoError(t, err)
-
-		require.Nil(t, ln.Close())
-
-		addr, config := lnMetadata(NetworkTCP, ln)
-
-		require.Equal(t, ln.Addr().String(), addr)
-		require.True(t, config == nil)
-	})
-
-	t.Run("non tls", func(t *testing.T) {
-		ln, err := net.Listen(NetworkTCP, ":0")
-
-		require.NoError(t, err)
-
-		addr, config := lnMetadata(NetworkTCP4, ln)
-
-		require.Equal(t, ln.Addr().String(), addr)
-		require.True(t, config == nil)
-	})
-
-	t.Run("tls", func(t *testing.T) {
-		cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")
-		require.NoError(t, err)
-
-		config := &tls.Config{Certificates: []tls.Certificate{cer}}
-
-		ln, err := net.Listen(NetworkTCP4, ":0")
-		require.NoError(t, err)
-
-		ln = tls.NewListener(ln, config)
-
-		addr, config := lnMetadata(NetworkTCP4, ln)
-
-		require.Equal(t, ln.Addr().String(), addr)
-		require.True(t, config != nil)
-	})
-}
-
 // go test -v -run=^$ -bench=Benchmark_SlashRecognition -benchmem -count=4
 func Benchmark_SlashRecognition(b *testing.B) {
 	search := "wtf/1234"
diff --git a/hooks_test.go b/hooks_test.go
index 52ff08a6..611dd93e 100644
--- a/hooks_test.go
+++ b/hooks_test.go
@@ -42,7 +42,8 @@ func Test_Hook_OnName(t *testing.T) {
 	defer bytebufferpool.Put(buf)
 
 	app.Hooks().OnName(func(r Route) error {
-		buf.WriteString(r.Name)
+		_, err := buf.WriteString(r.Name)
+		require.NoError(t, nil, err)
 
 		return nil
 	})
@@ -84,8 +85,8 @@ func Test_Hook_OnGroup(t *testing.T) {
 	defer bytebufferpool.Put(buf)
 
 	app.Hooks().OnGroup(func(g Group) error {
-		buf.WriteString(g.Prefix)
-
+		_, err := buf.WriteString(g.Prefix)
+		require.NoError(t, nil, err)
 		return nil
 	})
 
@@ -104,7 +105,8 @@ func Test_Hook_OnGroupName(t *testing.T) {
 	defer bytebufferpool.Put(buf)
 
 	app.Hooks().OnGroupName(func(g Group) error {
-		buf.WriteString(g.name)
+		_, err := buf.WriteString(g.name)
+		require.NoError(t, nil, err)
 
 		return nil
 	})
@@ -143,7 +145,8 @@ func Test_Hook_OnShutdown(t *testing.T) {
 	defer bytebufferpool.Put(buf)
 
 	app.Hooks().OnShutdown(func() error {
-		buf.WriteString("shutdowning")
+		_, err := buf.WriteString("shutdowning")
+		require.NoError(t, nil, err)
 
 		return nil
 	})
@@ -155,15 +158,14 @@ func Test_Hook_OnShutdown(t *testing.T) {
 func Test_Hook_OnListen(t *testing.T) {
 	t.Parallel()
 
-	app := New(Config{
-		DisableStartupMessage: true,
-	})
+	app := New()
 
 	buf := bytebufferpool.Get()
 	defer bytebufferpool.Put(buf)
 
 	app.Hooks().OnListen(func() error {
-		buf.WriteString("ready")
+		_, err := buf.WriteString("ready")
+		require.NoError(t, nil, err)
 
 		return nil
 	})
@@ -172,8 +174,8 @@ func Test_Hook_OnListen(t *testing.T) {
 		time.Sleep(1000 * time.Millisecond)
 		require.Nil(t, app.Shutdown())
 	}()
-	require.Nil(t, app.Listen(":9000"))
 
+	require.Nil(t, app.Listen(":9000", ListenConfig{DisableStartupMessage: true}))
 	require.Equal(t, "ready", buf.String())
 }
 
@@ -194,5 +196,5 @@ func Test_Hook_OnHook(t *testing.T) {
 		return nil
 	})
 
-	require.Nil(t, app.prefork(NetworkTCP4, ":3000", nil))
+	require.Nil(t, app.prefork(":3000", nil, ListenConfig{DisableStartupMessage: true, EnablePrefork: true}))
 }
diff --git a/listen_test.go b/listen_test.go
index 2f6f6718..de482e43 100644
--- a/listen_test.go
+++ b/listen_test.go
@@ -1,15 +1,14 @@
-// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
-// 🤖 Github Repository: https://github.com/gofiber/fiber
-// 📌 API Documentation: https://docs.gofiber.io
-
 package fiber
 
 import (
 	"bytes"
+	"context"
 	"crypto/tls"
+	"errors"
 	"fmt"
 	"io"
 	"log"
+	"net"
 	"os"
 	"strings"
 	"sync"
@@ -20,9 +19,9 @@ import (
 	"github.com/valyala/fasthttp/fasthttputil"
 )
 
-// go test -run Test_App_Listen
-func Test_App_Listen(t *testing.T) {
-	app := New(Config{DisableStartupMessage: true})
+// go test -run Test_Listen
+func Test_Listen(t *testing.T) {
+	app := New()
 
 	require.False(t, app.Listen(":99999") == nil)
 
@@ -31,78 +30,181 @@ func Test_App_Listen(t *testing.T) {
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.Listen(":4003"))
+	require.Nil(t, app.Listen(":4003", ListenConfig{DisableStartupMessage: true}))
+}
+
+// go test -run Test_Listen_Graceful_Shutdown
+func Test_Listen_Graceful_Shutdown(t *testing.T) {
+	var mu sync.Mutex
+	var shutdown bool
+
+	app := New()
+
+	app.Get("/", func(c Ctx) error {
+		return c.SendString(c.Hostname())
+	})
+
+	ln := fasthttputil.NewInmemoryListener()
+
+	go func() {
+		ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond)
+		defer cancel()
+
+		err := app.Listener(ln, ListenConfig{
+			DisableStartupMessage: true,
+			GracefulContext:       ctx,
+			OnShutdownSuccess: func() {
+				mu.Lock()
+				shutdown = true
+				mu.Unlock()
+			},
+		})
+
+		require.NoError(t, err)
+	}()
+
+	testCases := []struct {
+		Time               time.Duration
+		ExpectedBody       string
+		ExpectedStatusCode int
+		ExceptedErrsLen    int
+	}{
+		{Time: 100 * time.Millisecond, ExpectedBody: "example.com", ExpectedStatusCode: StatusOK, ExceptedErrsLen: 0},
+		{Time: 500 * time.Millisecond, ExpectedBody: "", ExpectedStatusCode: 0, ExceptedErrsLen: 1},
+	}
+
+	for _, tc := range testCases {
+		time.Sleep(tc.Time)
+
+		a := Get("http://example.com")
+		a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() }
+		code, body, errs := a.String()
+
+		require.Equal(t, tc.ExpectedStatusCode, code)
+		require.Equal(t, tc.ExpectedBody, body)
+		require.Equal(t, tc.ExceptedErrsLen, len(errs))
+	}
+
+	mu.Lock()
+	require.True(t, shutdown)
+	mu.Unlock()
 }
 
-// go test -run Test_App_Listen_Prefork
-func Test_App_Listen_Prefork(t *testing.T) {
+// go test -run Test_Listen_Prefork
+func Test_Listen_Prefork(t *testing.T) {
 	testPreforkMaster = true
 
-	app := New(Config{DisableStartupMessage: true, Prefork: true})
+	app := New()
 
-	require.Nil(t, app.Listen(":99999"))
+	require.Nil(t, app.Listen(":99999", ListenConfig{DisableStartupMessage: true, EnablePrefork: true}))
 }
 
-// go test -run Test_App_ListenTLS
-func Test_App_ListenTLS(t *testing.T) {
+// go test -run Test_Listen_TLS
+func Test_Listen_TLS(t *testing.T) {
 	app := New()
 
 	// invalid port
-	require.False(t, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil)
-	// missing perm/cert file
-	require.False(t, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil)
+	require.False(t, app.Listen(":99999", ListenConfig{
+		CertFile:    "./.github/testdata/ssl.pem",
+		CertKeyFile: "./.github/testdata/ssl.key",
+	}) == nil)
 
 	go func() {
 		time.Sleep(1000 * time.Millisecond)
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key"))
+	require.Nil(t, app.Listen(":0", ListenConfig{
+		CertFile:    "./.github/testdata/ssl.pem",
+		CertKeyFile: "./.github/testdata/ssl.key",
+	}))
+
 }
 
-// go test -run Test_App_ListenTLS_Prefork
-func Test_App_ListenTLS_Prefork(t *testing.T) {
+// go test -run Test_Listen_TLS_Prefork
+func Test_Listen_TLS_Prefork(t *testing.T) {
 	testPreforkMaster = true
 
-	app := New(Config{DisableStartupMessage: true, Prefork: true})
+	app := New()
 
 	// invalid key file content
-	require.False(t, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil)
+	require.False(t, app.Listen(":0", ListenConfig{
+		DisableStartupMessage: true,
+		EnablePrefork:         true,
+		CertFile:              "./.github/testdata/ssl.pem",
+		CertKeyFile:           "./.github/testdata/template.tmpl",
+	}) == nil)
+
+	go func() {
+		time.Sleep(1000 * time.Millisecond)
+		require.Nil(t, app.Shutdown())
+	}()
+
+	require.Nil(t, app.Listen(":99999", ListenConfig{
+		DisableStartupMessage: true,
+		EnablePrefork:         true,
+		CertFile:              "./.github/testdata/ssl.pem",
+		CertKeyFile:           "./.github/testdata/ssl.key",
+	}))
 
-	require.Nil(t, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key"))
 }
 
-// go test -run Test_App_ListenMutualTLS
-func Test_App_ListenMutualTLS(t *testing.T) {
+// go test -run Test_Listen_MutualTLS
+func Test_Listen_MutualTLS(t *testing.T) {
 	app := New()
 
 	// invalid port
-	require.False(t, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil)
-	// missing perm/cert file
-	require.False(t, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil)
+	require.False(t, app.Listen(":99999", ListenConfig{
+		CertFile:       "./.github/testdata/ssl.pem",
+		CertKeyFile:    "./.github/testdata/ssl.key",
+		CertClientFile: "./.github/testdata/ca-chain.cert.pem",
+	}) == nil)
 
 	go func() {
 		time.Sleep(1000 * time.Millisecond)
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem"))
+	require.Nil(t, app.Listen(":0", ListenConfig{
+		CertFile:       "./.github/testdata/ssl.pem",
+		CertKeyFile:    "./.github/testdata/ssl.key",
+		CertClientFile: "./.github/testdata/ca-chain.cert.pem",
+	}))
+
 }
 
-// go test -run Test_App_ListenMutualTLS_Prefork
-func Test_App_ListenMutualTLS_Prefork(t *testing.T) {
+// go test -run Test_Listen_MutualTLS_Prefork
+func Test_Listen_MutualTLS_Prefork(t *testing.T) {
 	testPreforkMaster = true
 
-	app := New(Config{DisableStartupMessage: true, Prefork: true})
+	app := New()
 
 	// invalid key file content
-	require.False(t, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil)
+	require.False(t, app.Listen(":0", ListenConfig{
+		DisableStartupMessage: true,
+		EnablePrefork:         true,
+		CertFile:              "./.github/testdata/ssl.pem",
+		CertKeyFile:           "./.github/testdata/template.html",
+		CertClientFile:        "./.github/testdata/ca-chain.cert.pem",
+	}) == nil)
+
+	go func() {
+		time.Sleep(1000 * time.Millisecond)
+		require.Nil(t, app.Shutdown())
+	}()
+
+	require.Nil(t, app.Listen(":99999", ListenConfig{
+		DisableStartupMessage: true,
+		EnablePrefork:         true,
+		CertFile:              "./.github/testdata/ssl.pem",
+		CertKeyFile:           "./.github/testdata/ssl.key",
+		CertClientFile:        "./.github/testdata/ca-chain.cert.pem",
+	}))
 
-	require.Nil(t, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem"))
 }
 
-// go test -run Test_App_Listener
-func Test_App_Listener(t *testing.T) {
+// go test -run Test_Listener
+func Test_Listener(t *testing.T) {
 	app := New()
 
 	go func() {
@@ -114,16 +216,6 @@ func Test_App_Listener(t *testing.T) {
 	require.Nil(t, app.Listener(ln))
 }
 
-// go test -run Test_App_Listener_Prefork
-func Test_App_Listener_Prefork(t *testing.T) {
-	testPreforkMaster = true
-
-	app := New(Config{DisableStartupMessage: true, Prefork: true})
-
-	ln := fasthttputil.NewInmemoryListener()
-	require.Nil(t, app.Listener(ln))
-}
-
 func Test_App_Listener_TLS_Listener(t *testing.T) {
 	// Create tls certificate
 	cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")
@@ -145,71 +237,180 @@ func Test_App_Listener_TLS_Listener(t *testing.T) {
 	require.Nil(t, app.Listener(ln))
 }
 
-func captureOutput(f func()) string {
-	reader, writer, err := os.Pipe()
-	if err != nil {
-		panic(err)
-	}
-	stdout := os.Stdout
-	stderr := os.Stderr
-	defer func() {
-		os.Stdout = stdout
-		os.Stderr = stderr
-		log.SetOutput(os.Stderr)
+// go test -run Test_Listen_TLSConfigFunc
+func Test_Listen_TLSConfigFunc(t *testing.T) {
+	var callTLSConfig bool
+	app := New()
+
+	go func() {
+		time.Sleep(1000 * time.Millisecond)
+		require.Nil(t, app.Shutdown())
 	}()
-	os.Stdout = writer
-	os.Stderr = writer
-	log.SetOutput(writer)
-	out := make(chan string)
-	wg := new(sync.WaitGroup)
-	wg.Add(1)
+
+	require.Nil(t, app.Listen(":0", ListenConfig{
+		DisableStartupMessage: true,
+		TLSConfigFunc: func(tlsConfig *tls.Config) {
+			callTLSConfig = true
+		},
+		CertFile:    "./.github/testdata/ssl.pem",
+		CertKeyFile: "./.github/testdata/ssl.key",
+	}))
+
+	require.True(t, callTLSConfig)
+}
+
+// go test -run Test_Listen_ListenerAddrFunc
+func Test_Listen_ListenerAddrFunc(t *testing.T) {
+	var network string
+	app := New()
+
 	go func() {
-		var buf bytes.Buffer
-		wg.Done()
-		io.Copy(&buf, reader)
-		out <- buf.String()
+		time.Sleep(1000 * time.Millisecond)
+		require.Nil(t, app.Shutdown())
 	}()
-	wg.Wait()
-	f()
-	writer.Close()
-	return <-out
+
+	require.Nil(t, app.Listen(":0", ListenConfig{
+		DisableStartupMessage: true,
+		ListenerAddrFunc: func(addr net.Addr) {
+			network = addr.Network()
+		},
+		CertFile:    "./.github/testdata/ssl.pem",
+		CertKeyFile: "./.github/testdata/ssl.key",
+	}))
+
+	require.Equal(t, "tcp", network)
+}
+
+// go test -run Test_Listen_BeforeServeFunc
+func Test_Listen_BeforeServeFunc(t *testing.T) {
+	var handlers uint32
+	app := New()
+
+	go func() {
+		time.Sleep(1000 * time.Millisecond)
+		require.Nil(t, app.Shutdown())
+	}()
+
+	require.Equal(t, errors.New("test"), app.Listen(":0", ListenConfig{
+		DisableStartupMessage: true,
+		BeforeServeFunc: func(fiber *App) error {
+			handlers = fiber.HandlersCount()
+
+			return errors.New("test")
+		},
+	}))
+
+	require.Equal(t, uint32(0), handlers)
+}
+
+// go test -run Test_Listen_ListenerNetwork
+func Test_Listen_ListenerNetwork(t *testing.T) {
+	var network string
+	app := New()
+
+	go func() {
+		time.Sleep(1000 * time.Millisecond)
+		require.Nil(t, app.Shutdown())
+	}()
+
+	require.Nil(t, app.Listen(":0", ListenConfig{
+		DisableStartupMessage: true,
+		ListenerNetwork:       NetworkTCP6,
+		ListenerAddrFunc: func(addr net.Addr) {
+			network = addr.String()
+		},
+	}))
+
+	require.True(t, strings.Contains(network, "[::]:"))
+
+	go func() {
+		time.Sleep(1000 * time.Millisecond)
+		require.Nil(t, app.Shutdown())
+	}()
+
+	require.Nil(t, app.Listen(":0", ListenConfig{
+		DisableStartupMessage: true,
+		ListenerNetwork:       NetworkTCP4,
+		ListenerAddrFunc: func(addr net.Addr) {
+			network = addr.String()
+		},
+	}))
+
+	require.True(t, strings.Contains(network, "0.0.0.0:"))
 }
 
-func Test_App_Master_Process_Show_Startup_Message(t *testing.T) {
+// go test -run Test_Listen_Master_Process_Show_Startup_Message
+func Test_Listen_Master_Process_Show_Startup_Message(t *testing.T) {
+	cfg := ListenConfig{
+		EnablePrefork: true,
+	}
+
 	startupMessage := captureOutput(func() {
-		New(Config{Prefork: true}).
-			startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10))
+		New().
+			startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10), cfg)
 	})
+	colors := Colors{}
 	fmt.Println(startupMessage)
 	require.True(t, strings.Contains(startupMessage, "https://127.0.0.1:3000"))
 	require.True(t, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)"))
 	require.True(t, strings.Contains(startupMessage, "Child PIDs"))
 	require.True(t, strings.Contains(startupMessage, "11111, 22222, 33333, 44444, 55555, 60000"))
-	require.True(t, strings.Contains(startupMessage, "Prefork ........ Enabled"))
+	require.True(t, strings.Contains(startupMessage, fmt.Sprintf("Prefork: %sEnabled%s", colors.Blue, colors.Reset)))
 }
 
-func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) {
-	app := New(Config{Prefork: true, AppName: "Test App v1.0.1"})
+// go test -run Test_Listen_Master_Process_Show_Startup_MessageWithAppName
+func Test_Listen_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) {
+	cfg := ListenConfig{
+		EnablePrefork: true,
+	}
+
+	app := New(Config{AppName: "Test App v3.0.0"})
 	startupMessage := captureOutput(func() {
-		app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10))
+		app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10), cfg)
 	})
 	fmt.Println(startupMessage)
-	require.Equal(t, "Test App v1.0.1", app.Config().AppName)
+	require.Equal(t, "Test App v3.0.0", app.Config().AppName)
 	require.True(t, strings.Contains(startupMessage, app.Config().AppName))
 }
 
-func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing.T) {
+// go test -run Test_Listen_Master_Process_Show_Startup_MessageWithAppNameNonAscii
+func Test_Listen_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing.T) {
+	cfg := ListenConfig{
+		EnablePrefork: true,
+	}
+
 	appName := "Serveur de vérification des données"
-	app := New(Config{Prefork: true, AppName: appName})
+	app := New(Config{AppName: appName})
+
 	startupMessage := captureOutput(func() {
-		app.startupMessage(":3000", false, "")
+		app.startupMessage(":3000", false, "", cfg)
 	})
 	fmt.Println(startupMessage)
-	require.True(t, strings.Contains(startupMessage, "│        Serveur de vérification des données        │"))
+	require.True(t, strings.Contains(startupMessage, "Serveur de vérification des données"))
 }
 
-func Test_App_print_Route(t *testing.T) {
-	app := New(Config{EnablePrintRoutes: true})
+// go test -run Test_Listen_Master_Process_Show_Startup_MessageWithDisabledPreforkAndCustomEndpoint
+func Test_Listen_Master_Process_Show_Startup_MessageWithDisabledPreforkAndCustomEndpoint(t *testing.T) {
+	cfg := ListenConfig{
+		EnablePrefork: false,
+	}
+
+	appName := "Fiber Example Application"
+	app := New(Config{AppName: appName})
+	startupMessage := captureOutput(func() {
+		app.startupMessage("server.com:8081", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 5), cfg)
+	})
+	colors := Colors{}
+	fmt.Println(startupMessage)
+	require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%sINFO%s", colors.Green, colors.Reset)))
+	require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%s%s%s", colors.Blue, appName, colors.Reset)))
+	require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%s%s%s", colors.Blue, "https://server.com:8081", colors.Reset)))
+	require.True(t, strings.Contains(startupMessage, fmt.Sprintf("Prefork: %sDisabled%s", colors.Red, colors.Reset)))
+}
+
+// go test -run Test_Listen_Print_Route
+func Test_Listen_Print_Route(t *testing.T) {
+	app := New()
 	app.Get("/", emptyHandler).Name("routeName")
 	printRoutesMessage := captureOutput(func() {
 		app.printRoutesMessage()
@@ -221,8 +422,9 @@ func Test_App_print_Route(t *testing.T) {
 	require.True(t, strings.Contains(printRoutesMessage, "routeName"))
 }
 
-func Test_App_print_Route_with_group(t *testing.T) {
-	app := New(Config{EnablePrintRoutes: true})
+// go test -run Test_Listen_Print_Route_With_Group
+func Test_Listen_Print_Route_With_Group(t *testing.T) {
+	app := New()
 	app.Get("/", emptyHandler)
 
 	v1 := app.Group("v1")
@@ -244,6 +446,42 @@ func Test_App_print_Route_with_group(t *testing.T) {
 	require.True(t, strings.Contains(printRoutesMessage, "/v1/test/fiber/*"))
 }
 
-func emptyHandler(c Ctx) error {
+func captureOutput(f func()) string {
+	reader, writer, err := os.Pipe()
+	if err != nil {
+		panic(err)
+	}
+	stdout := os.Stdout
+	stderr := os.Stderr
+	defer func() {
+		os.Stdout = stdout
+		os.Stderr = stderr
+		log.SetOutput(os.Stderr)
+	}()
+	os.Stdout = writer
+	os.Stderr = writer
+	log.SetOutput(writer)
+	out := make(chan string)
+	wg := new(sync.WaitGroup)
+	wg.Add(1)
+	go func() {
+		var buf bytes.Buffer
+		wg.Done()
+		_, err := io.Copy(&buf, reader)
+		if err != nil {
+			panic(err)
+		}
+		out <- buf.String()
+	}()
+	wg.Wait()
+	f()
+	err = writer.Close()
+	if err != nil {
+		panic(err)
+	}
+	return <-out
+}
+
+func emptyHandler(_ Ctx) error {
 	return nil
 }
diff --git a/middleware/adaptor/adopter_test.go b/middleware/adaptor/adopter_test.go
index 6480bff8..8e9531e7 100644
--- a/middleware/adaptor/adopter_test.go
+++ b/middleware/adaptor/adopter_test.go
@@ -245,8 +245,8 @@ func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.A
 		if contentLength != expectedContentLength {
 			t.Fatalf("unexpected contentLength %d. Expecting %d", contentLength, expectedContentLength)
 		}
-		if c.Hostname() != expectedHost {
-			t.Fatalf("unexpected host %q. Expecting %q", c.Hostname(), expectedHost)
+		if c.Host() != expectedHost {
+			t.Fatalf("unexpected host %q. Expecting %q", c.Host(), expectedHost)
 		}
 		remoteAddr := c.Context().RemoteAddr().String()
 		if remoteAddr != expectedRemoteAddr {
diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go
index 2aa9eb36..9dba9571 100644
--- a/middleware/cache/cache_test.go
+++ b/middleware/cache/cache_test.go
@@ -128,7 +128,10 @@ func Test_Cache_WithSeveralRequests(t *testing.T) {
 				rsp, err := app.Test(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%d", id), nil))
 				require.NoError(t, err)
 
-				defer rsp.Body.Close()
+				defer func(Body io.ReadCloser) {
+					err := Body.Close()
+					require.NoError(t, err)
+				}(rsp.Body)
 
 				idFromServ, err := io.ReadAll(rsp.Body)
 				require.NoError(t, err)
@@ -413,71 +416,6 @@ func Test_CacheHeader(t *testing.T) {
 	require.Equal(t, cacheUnreachable, errRespCached.Header.Get("X-Cache"))
 }
 
-func Test_Cache_WithHead(t *testing.T) {
-	t.Parallel()
-
-	app := fiber.New()
-	app.Use(New())
-
-	app.Get("/", func(c fiber.Ctx) error {
-		now := fmt.Sprintf("%d", time.Now().UnixNano())
-		return c.SendString(now)
-	})
-
-	req := httptest.NewRequest("HEAD", "/", nil)
-	resp, err := app.Test(req)
-	require.Equal(t, cacheMiss, resp.Header.Get("X-Cache"))
-
-	cachedReq := httptest.NewRequest("HEAD", "/", nil)
-	cachedResp, err := app.Test(cachedReq)
-	require.Equal(t, cacheHit, cachedResp.Header.Get("X-Cache"))
-
-	body, err := io.ReadAll(resp.Body)
-	require.NoError(t, err)
-	cachedBody, err := io.ReadAll(cachedResp.Body)
-	require.NoError(t, err)
-
-	require.Equal(t, cachedBody, body)
-}
-
-func Test_Cache_WithHeadThenGet(t *testing.T) {
-	t.Parallel()
-
-	app := fiber.New()
-	app.Use(New())
-	app.Get("/", func(c fiber.Ctx) error {
-		return c.SendString(c.Query("cache"))
-	})
-
-	headResp, err := app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil))
-	require.NoError(t, err)
-	headBody, err := io.ReadAll(headResp.Body)
-	require.NoError(t, err)
-	require.Equal(t, "", string(headBody))
-	require.Equal(t, cacheMiss, headResp.Header.Get("X-Cache"))
-
-	headResp, err = app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil))
-	require.NoError(t, err)
-	headBody, err = io.ReadAll(headResp.Body)
-	require.NoError(t, err)
-	require.Equal(t, "", string(headBody))
-	require.Equal(t, cacheHit, headResp.Header.Get("X-Cache"))
-
-	getResp, err := app.Test(httptest.NewRequest("GET", "/?cache=123", nil))
-	require.NoError(t, err)
-	getBody, err := io.ReadAll(getResp.Body)
-	require.NoError(t, err)
-	require.Equal(t, "123", string(getBody))
-	require.Equal(t, cacheMiss, getResp.Header.Get("X-Cache"))
-
-	getResp, err = app.Test(httptest.NewRequest("GET", "/?cache=123", nil))
-	require.NoError(t, err)
-	getBody, err = io.ReadAll(getResp.Body)
-	require.NoError(t, err)
-	require.Equal(t, "123", string(getBody))
-	require.Equal(t, cacheHit, getResp.Header.Get("X-Cache"))
-}
-
 func Test_CustomCacheHeader(t *testing.T) {
 	t.Parallel()
 
diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go
index b83b7fe3..faf92552 100644
--- a/middleware/csrf/csrf_test.go
+++ b/middleware/csrf/csrf_test.go
@@ -41,7 +41,7 @@ func Test_CSRF(t *testing.T) {
 		ctx.Request.Reset()
 		ctx.Response.Reset()
 		ctx.Request.Header.SetMethod("POST")
-		ctx.Request.Header.Set("X-CSRF-Token", "johndoe")
+		ctx.Request.Header.Set(HeaderName, "johndoe")
 		h(ctx)
 		require.Equal(t, 403, ctx.Response.StatusCode())
 
@@ -56,7 +56,7 @@ func Test_CSRF(t *testing.T) {
 		ctx.Request.Reset()
 		ctx.Response.Reset()
 		ctx.Request.Header.SetMethod("POST")
-		ctx.Request.Header.Set("X-CSRF-Token", token)
+		ctx.Request.Header.Set(HeaderName, token)
 		h(ctx)
 		require.Equal(t, 200, ctx.Response.StatusCode())
 	}
@@ -237,6 +237,50 @@ func Test_CSRF_From_Cookie(t *testing.T) {
 	require.Equal(t, "OK", string(ctx.Response.Body()))
 }
 
+func Test_CSRF_From_Custom(t *testing.T) {
+	app := fiber.New()
+
+	extractor := func(c fiber.Ctx) (string, error) {
+		body := string(c.Body())
+		// Generate the correct extractor to get the token from the correct location
+		selectors := strings.Split(body, "=")
+
+		if len(selectors) != 2 || selectors[1] == "" {
+			return "", errMissingParam
+		}
+		return selectors[1], nil
+	}
+
+	app.Use(New(Config{Extractor: extractor}))
+
+	app.Post("/", func(c fiber.Ctx) error {
+		return c.SendStatus(fiber.StatusOK)
+	})
+
+	h := app.Handler()
+	ctx := &fasthttp.RequestCtx{}
+
+	// Invalid CSRF token
+	ctx.Request.Header.SetMethod("POST")
+	ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain)
+	h(ctx)
+	require.Equal(t, 403, ctx.Response.StatusCode())
+
+	// Generate CSRF token
+	ctx.Request.Reset()
+	ctx.Response.Reset()
+	ctx.Request.Header.SetMethod("GET")
+	h(ctx)
+	token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie))
+	token = strings.Split(strings.Split(token, ";")[0], "=")[1]
+
+	ctx.Request.Header.SetMethod("POST")
+	ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain)
+	ctx.Request.SetBodyString("_csrf=" + token)
+	h(ctx)
+	require.Equal(t, 200, ctx.Response.StatusCode())
+}
+
 func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) {
 	app := fiber.New()
 
@@ -262,7 +306,7 @@ func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) {
 	ctx.Request.Reset()
 	ctx.Response.Reset()
 	ctx.Request.Header.SetMethod("POST")
-	ctx.Request.Header.Set("X-CSRF-Token", "johndoe")
+	ctx.Request.Header.Set(HeaderName, "johndoe")
 	h(ctx)
 	require.Equal(t, 419, ctx.Response.StatusCode())
 	require.Equal(t, "invalid CSRF token", string(ctx.Response.Body()))
@@ -297,3 +341,111 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) {
 	require.Equal(t, 419, ctx.Response.StatusCode())
 	require.Equal(t, "empty CSRF token", string(ctx.Response.Body()))
 }
+
+// TODO: use this test case and make the unsafe header value bug from https://github.com/gofiber/fiber/issues/2045 reproducible and permanently fixed/tested by this testcase
+//func Test_CSRF_UnsafeHeaderValue(t *testing.T) {
+//	app := fiber.New()
+//
+//	app.Use(New())
+//	app.Get("/", func(c fiber.Ctx) error {
+//		return c.SendStatus(fiber.StatusOK)
+//	})
+//	app.Get("/test", func(c fiber.Ctx) error {
+//		return c.SendStatus(fiber.StatusOK)
+//	})
+//	app.Post("/", func(c fiber.Ctx) error {
+//		return c.SendStatus(fiber.StatusOK)
+//	})
+//
+//	resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil))
+//	utils.AssertEqual(t, nil, err)
+//	utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
+//
+//	var token string
+//	for _, c := range resp.Cookies() {
+//		if c.Name != ConfigDefault.CookieName {
+//			continue
+//		}
+//		token = c.Value
+//		break
+//	}
+//
+//	fmt.Println("token", token)
+//
+//	getReq := httptest.NewRequest(http.MethodGet, "/", nil)
+//	getReq.Header.Set(HeaderName, token)
+//	resp, err = app.Test(getReq)
+//
+//	getReq = httptest.NewRequest(http.MethodGet, "/test", nil)
+//	getReq.Header.Set("X-Requested-With", "XMLHttpRequest")
+//	getReq.Header.Set(fiber.HeaderCacheControl, "no")
+//	getReq.Header.Set(HeaderName, token)
+//
+//	resp, err = app.Test(getReq)
+//
+//	getReq.Header.Set(fiber.HeaderAccept, "*/*")
+//	getReq.Header.Del(HeaderName)
+//	resp, err = app.Test(getReq)
+//
+//	postReq := httptest.NewRequest(http.MethodPost, "/", nil)
+//	postReq.Header.Set("X-Requested-With", "XMLHttpRequest")
+//	postReq.Header.Set(HeaderName, token)
+//	resp, err = app.Test(postReq)
+//}
+
+// go test -v -run=^$ -bench=Benchmark_Middleware_CSRF_Check -benchmem -count=4
+func Benchmark_Middleware_CSRF_Check(b *testing.B) {
+	app := fiber.New()
+
+	app.Use(New())
+	app.Get("/", func(c fiber.Ctx) error {
+		return c.SendStatus(fiber.StatusTeapot)
+	})
+
+	fctx := &fasthttp.RequestCtx{}
+	h := app.Handler()
+	ctx := &fasthttp.RequestCtx{}
+
+	// Generate CSRF token
+	ctx.Request.Header.SetMethod("GET")
+	h(ctx)
+	token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie))
+	token = strings.Split(strings.Split(token, ";")[0], "=")[1]
+
+	ctx.Request.Header.SetMethod("POST")
+	ctx.Request.Header.Set(HeaderName, token)
+
+	b.ReportAllocs()
+	b.ResetTimer()
+
+	for n := 0; n < b.N; n++ {
+		h(fctx)
+	}
+
+	require.Equal(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode())
+}
+
+// go test -v -run=^$ -bench=Benchmark_Middleware_CSRF_GenerateToken -benchmem -count=4
+func Benchmark_Middleware_CSRF_GenerateToken(b *testing.B) {
+	app := fiber.New()
+
+	app.Use(New())
+	app.Get("/", func(c fiber.Ctx) error {
+		return c.SendStatus(fiber.StatusTeapot)
+	})
+
+	fctx := &fasthttp.RequestCtx{}
+	h := app.Handler()
+	ctx := &fasthttp.RequestCtx{}
+
+	// Generate CSRF token
+	ctx.Request.Header.SetMethod("GET")
+	b.ReportAllocs()
+	b.ResetTimer()
+
+	for n := 0; n < b.N; n++ {
+		h(fctx)
+	}
+
+	require.Equal(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode())
+}
diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go
new file mode 100644
index 00000000..e46bf05d
--- /dev/null
+++ b/middleware/envvar/envvar_test.go
@@ -0,0 +1,133 @@
+package envvar
+
+import (
+	"encoding/json"
+	"io"
+	"net/http"
+	"testing"
+
+	"github.com/gofiber/fiber/v3"
+	"github.com/stretchr/testify/require"
+)
+
+func TestEnvVarStructWithExportVarsExcludeVars(t *testing.T) {
+	t.Setenv("testKey", "testEnvValue")
+	t.Setenv("anotherEnvKey", "anotherEnvVal")
+	t.Setenv("excludeKey", "excludeEnvValue")
+
+	vars := newEnvVar(Config{
+		ExportVars:  map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"},
+		ExcludeVars: map[string]string{"excludeKey": ""}})
+
+	require.Equal(t, vars.Vars["testKey"], "testEnvValue")
+	require.Equal(t, vars.Vars["testDefaultKey"], "testDefaultVal")
+	require.Equal(t, vars.Vars["excludeKey"], "")
+	require.Equal(t, vars.Vars["anotherEnvKey"], "")
+}
+
+func TestEnvVarHandler(t *testing.T) {
+	t.Setenv("testKey", "testVal")
+
+	expectedEnvVarResponse, _ := json.Marshal(
+		struct {
+			Vars map[string]string `json:"vars"`
+		}{
+			map[string]string{"testKey": "testVal"},
+		})
+
+	app := fiber.New()
+	app.Use("/envvars", New(Config{
+		ExportVars: map[string]string{"testKey": ""}}))
+
+	req, _ := http.NewRequest("GET", "http://localhost/envvars", nil)
+	resp, err := app.Test(req)
+	require.Equal(t, nil, err)
+
+	respBody, err := io.ReadAll(resp.Body)
+	require.Equal(t, nil, err)
+
+	require.Equal(t, expectedEnvVarResponse, respBody)
+}
+
+func TestEnvVarHandlerNotMatched(t *testing.T) {
+	app := fiber.New()
+	app.Use("/envvars", New(Config{
+		ExportVars: map[string]string{"testKey": ""}}))
+
+	app.Get("/another-path", func(ctx fiber.Ctx) error {
+		ctx.SendString("OK")
+		return nil
+	})
+
+	req, _ := http.NewRequest("GET", "http://localhost/another-path", nil)
+	resp, err := app.Test(req)
+	require.Equal(t, nil, err)
+
+	respBody, err := io.ReadAll(resp.Body)
+	require.Equal(t, nil, err)
+
+	require.Equal(t, []byte("OK"), respBody)
+}
+
+func TestEnvVarHandlerDefaultConfig(t *testing.T) {
+	t.Setenv("testEnvKey", "testEnvVal")
+
+	app := fiber.New()
+	app.Use("/envvars", New())
+
+	req, _ := http.NewRequest("GET", "http://localhost/envvars", nil)
+	resp, err := app.Test(req)
+	require.Equal(t, nil, err)
+
+	respBody, err := io.ReadAll(resp.Body)
+	require.Equal(t, nil, err)
+
+	var envVars EnvVar
+	require.Equal(t, nil, json.Unmarshal(respBody, &envVars))
+	val := envVars.Vars["testEnvKey"]
+	require.Equal(t, "testEnvVal", val)
+}
+
+func TestEnvVarHandlerMethod(t *testing.T) {
+	app := fiber.New()
+	app.Use("/envvars", New())
+
+	req, _ := http.NewRequest("POST", "http://localhost/envvars", nil)
+	resp, err := app.Test(req)
+	require.Equal(t, nil, err)
+	require.Equal(t, fiber.StatusMethodNotAllowed, resp.StatusCode)
+}
+
+func TestEnvVarHandlerSpecialValue(t *testing.T) {
+	testEnvKey := "testEnvKey"
+	fakeBase64 := "testBase64:TQ=="
+	t.Setenv(testEnvKey, fakeBase64)
+
+	app := fiber.New()
+	app.Use("/envvars", New())
+	app.Use("/envvars/export", New(Config{ExportVars: map[string]string{testEnvKey: ""}}))
+
+	req, _ := http.NewRequest("GET", "http://localhost/envvars", nil)
+	resp, err := app.Test(req)
+	require.Equal(t, nil, err)
+
+	respBody, err := io.ReadAll(resp.Body)
+	require.Equal(t, nil, err)
+
+	var envVars EnvVar
+	require.Equal(t, nil, json.Unmarshal(respBody, &envVars))
+	val := envVars.Vars[testEnvKey]
+	require.Equal(t, fakeBase64, val)
+
+	req, _ = http.NewRequest("GET", "http://localhost/envvars/export", nil)
+	resp, err = app.Test(req)
+	require.Equal(t, nil, err)
+
+	respBody, err = io.ReadAll(resp.Body)
+	require.Equal(t, nil, err)
+
+	var envVarsExport EnvVar
+	require.Equal(t, nil, json.Unmarshal(respBody, &envVarsExport))
+	val = envVarsExport.Vars[testEnvKey]
+	require.Equal(t, fakeBase64, val)
+}
diff --git a/middleware/pprof/pprof_test.go b/middleware/pprof/pprof_test.go
index b6766318..eac8b3ca 100644
--- a/middleware/pprof/pprof_test.go
+++ b/middleware/pprof/pprof_test.go
@@ -11,7 +11,7 @@ import (
 )
 
 func Test_Non_Pprof_Path(t *testing.T) {
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	app.Use(New())
 
@@ -29,7 +29,7 @@ func Test_Non_Pprof_Path(t *testing.T) {
 }
 
 func Test_Pprof_Index(t *testing.T) {
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	app.Use(New())
 
@@ -48,7 +48,7 @@ func Test_Pprof_Index(t *testing.T) {
 }
 
 func Test_Pprof_Subs(t *testing.T) {
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	app.Use(New())
 
@@ -75,7 +75,7 @@ func Test_Pprof_Subs(t *testing.T) {
 }
 
 func Test_Pprof_Other(t *testing.T) {
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	app.Use(New())
 
diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go
index 05005a92..b1debeee 100644
--- a/middleware/proxy/proxy_test.go
+++ b/middleware/proxy/proxy_test.go
@@ -19,18 +19,21 @@ import (
 func createProxyTestServer(handler fiber.Handler, t *testing.T) (*fiber.App, string) {
 	t.Helper()
 
-	target := fiber.New(fiber.Config{DisableStartupMessage: true})
+	target := fiber.New()
 	target.Get("/", handler)
 
 	ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0")
 	require.NoError(t, err)
 
+	addr := ln.Addr().String()
+
 	go func() {
-		require.Nil(t, target.Listener(ln))
+		require.Nil(t, target.Listener(ln, fiber.ListenConfig{
+			DisableStartupMessage: true,
+		}))
 	}()
 
 	time.Sleep(2 * time.Second)
-	addr := ln.Addr().String()
 
 	return target, addr
 }
@@ -77,7 +80,7 @@ func Test_Proxy(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
 
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	app.Use(Balancer(Config{Servers: []string{addr}}))
 
@@ -100,7 +103,7 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) {
 
 	ln = tls.NewListener(ln, serverTLSConf)
 
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	app.Get("/tlsbalaner", func(c fiber.Ctx) error {
 		return c.SendString("tls balancer")
@@ -115,7 +118,11 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) {
 		TlsConfig: clientTLSConf,
 	}))
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, fiber.ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	code, body, errs := fiber.Get("https://" + addr + "/tlsbalaner").TLSConfig(clientTLSConf).String()
 
@@ -140,13 +147,17 @@ func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) {
 
 	proxyServerLn = tls.NewListener(proxyServerLn, proxyServerTLSConf)
 
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	proxyAddr := proxyServerLn.Addr().String()
 
 	app.Use(Forward("http://" + targetAddr))
 
-	go func() { require.Nil(t, app.Listener(proxyServerLn)) }()
+	go func() {
+		require.Nil(t, app.Listener(proxyServerLn, fiber.ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	code, body, errs := fiber.Get("https://" + proxyAddr).
 		InsecureSkipVerify().
@@ -191,7 +202,7 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) {
 
 	ln = tls.NewListener(ln, serverTLSConf)
 
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 
 	app.Get("/tlsfwd", func(c fiber.Ctx) error {
 		return c.SendString("tls forward")
@@ -204,7 +215,11 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) {
 	WithTlsConfig(clientTLSConf)
 	app.Use(Forward("https://" + addr + "/tlsfwd"))
 
-	go func() { require.Nil(t, app.Listener(ln)) }()
+	go func() {
+		require.Nil(t, app.Listener(ln, fiber.ListenConfig{
+			DisableStartupMessage: true,
+		}))
+	}()
 
 	code, body, errs := fiber.Get("https://" + addr).TLSConfig(clientTLSConf).String()
 
@@ -372,7 +387,7 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) {
 		return c.SendString("hello world")
 	}, t)
 
-	app := fiber.New(fiber.Config{DisableStartupMessage: true})
+	app := fiber.New()
 	app.Get("/*", func(c fiber.Ctx) error {
 		path := c.OriginalURL()
 		url := strings.TrimPrefix(path, "/")
diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go
index 57628755..1ff951bc 100644
--- a/middleware/session/session_test.go
+++ b/middleware/session/session_test.go
@@ -123,9 +123,9 @@ func Test_Session_Types(t *testing.T) {
 		Name: "John",
 	}
 	// set value
-	var vbool bool = true
-	var vstring string = "str"
-	var vint int = 13
+	var vbool = true
+	var vstring = "str"
+	var vint = 13
 	var vint8 int8 = 13
 	var vint16 int16 = 13
 	var vint32 int32 = 13
diff --git a/path_test.go b/path_test.go
index 455e97fa..dca58ff1 100644
--- a/path_test.go
+++ b/path_test.go
@@ -476,7 +476,7 @@ func Test_Path_matchParams(t *testing.T) {
 		{url: "/api/v1/123", params: []string{"123"}, match: true},
 		{url: "/api/v1/12345", params: []string{"12345"}, match: true},
 	})
-	testCase("/api/v1/:param<exactLen(5)>", []testparams{
+	testCase("/api/v1/:param<len(5)>", []testparams{
 		{url: "/api/v1/ent", params: []string{"ent"}, match: false},
 		{url: "/api/v1/123", params: []string{"123"}, match: false},
 		{url: "/api/v1/12345", params: []string{"12345"}, match: true},
@@ -521,12 +521,18 @@ func Test_Path_matchParams(t *testing.T) {
 		{url: "/api/v1/8728382", params: []string{"8728382"}, match: false},
 		{url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true},
 	})
-	testCase("/api/v1/:param<regex(p\\([a\\-z]\\+\\)ch)>", []testparams{
+	testCase("/api/v1/:param<regex(p([a-z]+)ch)>", []testparams{
 		{url: "/api/v1/ent", params: []string{"ent"}, match: false},
 		{url: "/api/v1/15", params: []string{"15"}, match: false},
 		{url: "/api/v1/peach", params: []string{"peach"}, match: true},
 		{url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false},
 	})
+	testCase("/api/v1/:param<regex(\\d{4}-\\d{2}-\\d{2})}>", []testparams{
+		{url: "/api/v1/ent", params: []string{"ent"}, match: false},
+		{url: "/api/v1/15", params: []string{"15"}, match: false},
+		{url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true},
+		{url: "/api/v1/2022/08-27", params: []string{"p34ch"}, match: false},
+	})
 	testCase("/api/v1/:param<int;bool((>", []testparams{
 		{url: "/api/v1/entity", params: []string{"entity"}, match: false},
 		{url: "/api/v1/8728382", params: []string{"8728382"}, match: true},
@@ -550,6 +556,19 @@ func Test_Path_matchParams(t *testing.T) {
 		{url: "/api/v1/25", params: []string{"25"}, match: true},
 		{url: "/api/v1/true", params: []string{"true"}, match: false},
 	})
+	testCase("/api/v1/:param<int\\;range(10,30)>", []testparams{
+		{url: "/api/v1/entity", params: []string{"entity"}, match: true},
+		{url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true},
+		{url: "/api/v1/25", params: []string{"25"}, match: true},
+		{url: "/api/v1/true", params: []string{"true"}, match: true},
+	})
+	testCase("/api/v1/:param<range(10\\,30,1500)>", []testparams{
+		{url: "/api/v1/entity", params: []string{"entity"}, match: false},
+		{url: "/api/v1/87283827683", params: []string{"8728382"}, match: false},
+		{url: "/api/v1/25", params: []string{"25"}, match: true},
+		{url: "/api/v1/1200", params: []string{"1200"}, match: true},
+		{url: "/api/v1/true", params: []string{"true"}, match: false},
+	})
 }
 
 func Test_Utils_GetTrimmedParam(t *testing.T) {
@@ -682,7 +701,7 @@ func Benchmark_Path_matchParams(t *testing.B) {
 		{url: "/api/v1/123", params: []string{"123"}, match: true},
 		{url: "/api/v1/12345", params: []string{"12345"}, match: true},
 	})
-	benchCase("/api/v1/:param<exactLen(5)>", []testparams{
+	benchCase("/api/v1/:param<len(5)>", []testparams{
 		{url: "/api/v1/ent", params: []string{"ent"}, match: false},
 		{url: "/api/v1/123", params: []string{"123"}, match: false},
 		{url: "/api/v1/12345", params: []string{"12345"}, match: true},
@@ -727,12 +746,18 @@ func Benchmark_Path_matchParams(t *testing.B) {
 		{url: "/api/v1/8728382", params: []string{"8728382"}, match: false},
 		{url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true},
 	})
-	benchCase("/api/v1/:param<regex(p\\([a\\-z]\\+\\)ch)>", []testparams{
+	benchCase("/api/v1/:param<regex(p([a-z]+)ch)>", []testparams{
 		{url: "/api/v1/ent", params: []string{"ent"}, match: false},
 		{url: "/api/v1/15", params: []string{"15"}, match: false},
 		{url: "/api/v1/peach", params: []string{"peach"}, match: true},
 		{url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false},
 	})
+	benchCase("/api/v1/:param<regex(\\d{4}-\\d{2}-\\d{2})}>", []testparams{
+		{url: "/api/v1/ent", params: []string{"ent"}, match: false},
+		{url: "/api/v1/15", params: []string{"15"}, match: false},
+		{url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true},
+		{url: "/api/v1/2022/08-27", params: []string{"p34ch"}, match: false},
+	})
 	benchCase("/api/v1/:param<int;bool((>", []testparams{
 		{url: "/api/v1/entity", params: []string{"entity"}, match: false},
 		{url: "/api/v1/8728382", params: []string{"8728382"}, match: true},
@@ -756,28 +781,17 @@ func Benchmark_Path_matchParams(t *testing.B) {
 		{url: "/api/v1/25", params: []string{"25"}, match: true},
 		{url: "/api/v1/true", params: []string{"true"}, match: false},
 	})
-}
-
-func Test_Path_matchParams0(t *testing.T) {
-	t.Parallel()
-	type testparams struct {
-		url          string
-		params       []string
-		match        bool
-		partialCheck bool
-	}
-	var ctxParams [maxParams]string
-	testCase := func(r string, cases []testparams) {
-		parser := parseRoute(r)
-		for _, c := range cases {
-			match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck)
-			require.Equal(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url))
-			if match && len(c.params) > 0 {
-				require.Equal(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url))
-			}
-		}
-	}
-	testCase("/api/v1/:param<datetime(2006-01-02)>", []testparams{
-		{url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true},
+	benchCase("/api/v1/:param<int\\;range(10,30)>", []testparams{
+		{url: "/api/v1/entity", params: []string{"entity"}, match: true},
+		{url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true},
+		{url: "/api/v1/25", params: []string{"25"}, match: true},
+		{url: "/api/v1/true", params: []string{"true"}, match: true},
+	})
+	benchCase("/api/v1/:param<range(10\\,30,1500)>", []testparams{
+		{url: "/api/v1/entity", params: []string{"entity"}, match: false},
+		{url: "/api/v1/87283827683", params: []string{"8728382"}, match: false},
+		{url: "/api/v1/25", params: []string{"25"}, match: true},
+		{url: "/api/v1/1200", params: []string{"1200"}, match: true},
+		{url: "/api/v1/true", params: []string{"true"}, match: false},
 	})
 }
diff --git a/prefork_test.go b/prefork_test.go
index f6c04b9b..a1a680c1 100644
--- a/prefork_test.go
+++ b/prefork_test.go
@@ -23,7 +23,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) {
 
 	app := New()
 
-	err := app.prefork(NetworkTCP4, "invalid", nil)
+	err := app.prefork("invalid", nil, listenConfigDefault())
 	require.False(t, err == nil)
 
 	go func() {
@@ -31,7 +31,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) {
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.prefork(NetworkTCP6, "[::1]:", nil))
+	require.Nil(t, app.prefork("[::1]:", nil, ListenConfig{ListenerNetwork: NetworkTCP6}))
 
 	// Create tls certificate
 	cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")
@@ -45,7 +45,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) {
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.prefork(NetworkTCP4, "127.0.0.1:", config))
+	require.Nil(t, app.prefork("127.0.0.1:", config, listenConfigDefault()))
 }
 
 func Test_App_Prefork_Master_Process(t *testing.T) {
@@ -59,12 +59,14 @@ func Test_App_Prefork_Master_Process(t *testing.T) {
 		require.Nil(t, app.Shutdown())
 	}()
 
-	require.Nil(t, app.prefork(NetworkTCP4, ":3000", nil))
+	require.Nil(t, app.prefork(":3000", nil, listenConfigDefault()))
 
 	dummyChildCmd = "invalid"
 
-	err := app.prefork(NetworkTCP4, "127.0.0.1:", nil)
+	err := app.prefork("127.0.0.1:", nil, listenConfigDefault())
 	require.False(t, err == nil)
+
+	dummyChildCmd = "go"
 }
 
 func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) {
@@ -79,7 +81,7 @@ func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) {
 
 	os.Stdout = w
 
-	New().startupProcess().startupMessage(":3000", false, "")
+	New().startupProcess().startupMessage(":3000", false, "", listenConfigDefault())
 
 	require.Nil(t, w.Close())
 
diff --git a/router_test.go b/router_test.go
index 6a53e34e..bd66430b 100644
--- a/router_test.go
+++ b/router_test.go
@@ -462,7 +462,7 @@ func Benchmark_App_MethodNotAllowed(b *testing.B) {
 	}
 	b.StopTimer()
 	require.Equal(b, 405, c.Response.StatusCode())
-	require.Equal(b, "GET, HEAD", string(c.Response.Header.Peek("Allow")))
+	require.Equal(b, "GET", string(c.Response.Header.Peek("Allow")))
 	require.Equal(b, utils.StatusMessage(StatusMethodNotAllowed), string(c.Response.Body()))
 }