diff --git a/.github/workflows/cov.yml b/.github/workflows/cov.yml
index 010df083..8b8e3988 100644
--- a/.github/workflows/cov.yml
+++ b/.github/workflows/cov.yml
@@ -31,10 +31,10 @@ jobs:
         run: cargo llvm-cov clean --workspace
 
       - name: Code coverage (glommio)
-        run: cargo +nightly llvm-cov --no-report --all --no-default-features --features="glommio,cookie,url,compress,openssl,rustls,ws"
+        run: cargo +nightly llvm-cov --no-report --all --no-default-features --features="glommio,cookie,url,compress,openssl,rustls,ws,brotli"
 
       - name: Code coverage
-        run: cargo +nightly llvm-cov --no-report --all --doctests --no-default-features --features="tokio,cookie,url,compress,openssl,rustls,ws"
+        run: cargo +nightly llvm-cov --no-report --all --doctests --no-default-features --features="tokio,cookie,url,compress,openssl,rustls,ws,brotli"
 
       - name: Generate coverage report
         run: cargo +nightly llvm-cov report --lcov --output-path lcov.info --ignore-filename-regex="ntex-tokio|ntex-glommio|ntex-async-std"
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index a149cf57..a990fa92 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -32,24 +32,30 @@ jobs:
         with:
           command: generate-lockfile
 
-      # - name: Cache vcpkg
-      #   uses: actions/cache@v1
-      #   with:
-      #     path: C:\vcpkg\installed\x64-windows\
-      #     key: x86_64-pc-windows-msvc-openssl
+      - name: Cache vcpkg
+        uses: actions/cache@v4
+        with:
+          path: C:\vcpkg\installed\x64-windows\
+          key: x86_64-pc-windows-msvc-openssl
 
       - name: Cache cargo registry
-        uses: actions/cache@v1
+        uses: actions/cache@v4
         with:
           path: ~/.cargo/registry
           key: ${{ matrix.version }}-x86_64-pc-windows-msvc-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
 
       - name: Cache cargo index
-        uses: actions/cache@v1
+        uses: actions/cache@v4
         with:
           path: ~/.cargo/git
           key: ${{ matrix.version }}-x86_64-pc-windows-msvc-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
 
+      - name: Install cmake
+        uses: lukka/get-cmake@latest
+
+      - name: Install nasm
+        uses: ilammy/setup-nasm@v1
+
       - name: Install OpenSSL
         run: |
           vcpkg integrate install
diff --git a/ntex/CHANGES.md b/ntex/CHANGES.md
index ecbf0e38..621a9381 100644
--- a/ntex/CHANGES.md
+++ b/ntex/CHANGES.md
@@ -1,5 +1,11 @@
 # Changes
 
+## [1.2.1] - 2024-03-28
+
+* Feature gate websocket support #320
+
+* Feature gate brotli2 encoder
+
 ## [1.2.0] - 2024-03-24
 
 * Refactor server workers management
diff --git a/ntex/Cargo.toml b/ntex/Cargo.toml
index c1fabccd..449c3d8a 100644
--- a/ntex/Cargo.toml
+++ b/ntex/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ntex"
-version = "1.2.0"
+version = "1.2.1"
 authors = ["ntex contributors <team@ntex.rs>"]
 description = "Framework for composable network services"
 readme = "README.md"
@@ -17,14 +17,14 @@ license = "MIT OR Apache-2.0"
 edition = "2021"
 
 [package.metadata.docs.rs]
-features = ["tokio", "openssl", "rustls", "compress", "cookie"]
+features = ["tokio", "openssl", "rustls", "compress", "cookie", "ws"]
 
 [lib]
 name = "ntex"
 path = "src/lib.rs"
 
 [features]
-default = []
+default = ["ws"]
 
 # openssl
 openssl = ["tls-openssl", "ntex-tls/openssl"]
@@ -33,7 +33,7 @@ openssl = ["tls-openssl", "ntex-tls/openssl"]
 rustls = ["tls-rustls", "webpki-roots", "ntex-tls/rustls"]
 
 # enable compressison support
-compress = ["flate2", "brotli2"]
+compress = ["flate2"]
 
 # enable cookie support
 cookie = ["coo-kie", "coo-kie/percent-encode"]
@@ -53,6 +53,9 @@ async-std = ["ntex-net/async-std"]
 # websocket support
 ws = ["dep:sha-1"]
 
+# brotli2 support
+brotli = ["dep:brotli2"]
+
 [dependencies]
 ntex-codec = "0.6.2"
 ntex-http = "0.1.12"
@@ -62,24 +65,24 @@ ntex-macros = "0.1.3"
 ntex-util = "1.0.1"
 ntex-bytes = "0.1.24"
 ntex-server = "1.0.0"
-ntex-h2 = "0.5.1"
-ntex-rt = "0.4.11"
+ntex-h2 = "0.5.2"
+ntex-rt = "0.4.12"
 ntex-io = "1.0.1"
 ntex-net = "1.0.0"
-ntex-tls = "1.0.0"
+ntex-tls = "1.1.0"
 
 base64 = "0.22"
 bitflags = "2"
 log = "0.4"
-nanorand = { version = "0.7", default-features = false, features = [
-    "std",
-    "wyrand",
-] }
 pin-project-lite = "0.2"
 regex = { version = "1.10", default-features = false, features = ["std"] }
 serde = { version = "1.0", features = ["derive"] }
 sha-1 = { version = "0.10", optional = true }
 thiserror = "1.0"
+nanorand = { version = "0.7", default-features = false, features = [
+    "std",
+    "wyrand",
+] }
 
 # http/web framework
 httparse = "1.8"
diff --git a/ntex/src/http/encoding/decoder.rs b/ntex/src/http/encoding/decoder.rs
index 12de36e1..5a518738 100644
--- a/ntex/src/http/encoding/decoder.rs
+++ b/ntex/src/http/encoding/decoder.rs
@@ -1,5 +1,6 @@
 use std::{future::Future, io, io::Write, pin::Pin, task::Context, task::Poll};
 
+#[cfg(feature = "brotli")]
 use brotli2::write::BrotliDecoder;
 use flate2::write::{GzDecoder, ZlibDecoder};
 
@@ -26,6 +27,7 @@ where
     #[inline]
     pub fn new(stream: S, encoding: ContentEncoding) -> Decoder<S> {
         let decoder = match encoding {
+            #[cfg(feature = "brotli")]
             ContentEncoding::Br => Some(ContentDecoder::Br(Box::new(BrotliDecoder::new(
                 Writer::new(),
             )))),
@@ -135,12 +137,14 @@ where
 enum ContentDecoder {
     Deflate(Box<ZlibDecoder<Writer>>),
     Gzip(Box<GzDecoder<Writer>>),
+    #[cfg(feature = "brotli")]
     Br(Box<BrotliDecoder<Writer>>),
 }
 
 impl ContentDecoder {
     fn feed_eof(&mut self) -> io::Result<Option<Bytes>> {
         match self {
+            #[cfg(feature = "brotli")]
             ContentDecoder::Br(ref mut decoder) => match decoder.flush() {
                 Ok(()) => {
                     let b = decoder.get_mut().take();
@@ -179,6 +183,7 @@ impl ContentDecoder {
 
     fn feed_data(&mut self, data: Bytes) -> io::Result<Option<Bytes>> {
         match self {
+            #[cfg(feature = "brotli")]
             ContentDecoder::Br(ref mut decoder) => match decoder.write_all(&data) {
                 Ok(_) => {
                     decoder.flush()?;
diff --git a/ntex/src/http/encoding/encoder.rs b/ntex/src/http/encoding/encoder.rs
index 7678a553..92003e60 100644
--- a/ntex/src/http/encoding/encoder.rs
+++ b/ntex/src/http/encoding/encoder.rs
@@ -1,6 +1,7 @@
 //! Stream encoder
 use std::{fmt, future::Future, io, io::Write, pin::Pin, task::Context, task::Poll};
 
+#[cfg(feature = "brotli")]
 use brotli2::write::BrotliEncoder;
 use flate2::write::{GzEncoder, ZlibEncoder};
 
@@ -190,15 +191,23 @@ fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) {
 enum ContentEncoder {
     Deflate(ZlibEncoder<Writer>),
     Gzip(GzEncoder<Writer>),
+    #[cfg(feature = "brotli")]
     Br(BrotliEncoder<Writer>),
 }
 
 impl ContentEncoder {
     fn can_encode(encoding: ContentEncoding) -> bool {
-        matches!(
-            encoding,
-            ContentEncoding::Deflate | ContentEncoding::Gzip | ContentEncoding::Br
-        )
+        #[cfg(feature = "brotli")]
+        {
+            matches!(
+                encoding,
+                ContentEncoding::Deflate | ContentEncoding::Gzip | ContentEncoding::Br
+            )
+        }
+        #[cfg(not(feature = "brotli"))]
+        {
+            matches!(encoding, ContentEncoding::Deflate | ContentEncoding::Gzip)
+        }
     }
 
     fn encoder(encoding: ContentEncoding) -> Option<Self> {
@@ -211,6 +220,7 @@ impl ContentEncoder {
                 Writer::new(),
                 flate2::Compression::fast(),
             ))),
+            #[cfg(feature = "brotli")]
             ContentEncoding::Br => {
                 Some(ContentEncoder::Br(BrotliEncoder::new(Writer::new(), 3)))
             }
@@ -220,6 +230,7 @@ impl ContentEncoder {
 
     fn take(&mut self) -> Bytes {
         match *self {
+            #[cfg(feature = "brotli")]
             ContentEncoder::Br(ref mut encoder) => encoder.get_mut().take(),
             ContentEncoder::Deflate(ref mut encoder) => encoder.get_mut().take(),
             ContentEncoder::Gzip(ref mut encoder) => encoder.get_mut().take(),
@@ -228,6 +239,7 @@ impl ContentEncoder {
 
     fn finish(self) -> Result<Bytes, io::Error> {
         match self {
+            #[cfg(feature = "brotli")]
             ContentEncoder::Br(encoder) => match encoder.finish() {
                 Ok(writer) => Ok(writer.buf.freeze()),
                 Err(err) => Err(err),
@@ -245,6 +257,7 @@ impl ContentEncoder {
 
     fn write(&mut self, data: &[u8]) -> Result<(), io::Error> {
         match *self {
+            #[cfg(feature = "brotli")]
             ContentEncoder::Br(ref mut encoder) => match encoder.write_all(data) {
                 Ok(_) => Ok(()),
                 Err(err) => {
@@ -275,6 +288,7 @@ impl fmt::Debug for ContentEncoder {
         match self {
             ContentEncoder::Deflate(_) => write!(f, "ContentEncoder::Deflate"),
             ContentEncoder::Gzip(_) => write!(f, "ContentEncoder::Gzip"),
+            #[cfg(feature = "brotli")]
             ContentEncoder::Br(_) => write!(f, "ContentEncoder::Br"),
         }
     }