diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f12202017..fdd6f0f9a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -56,6 +56,7 @@ jobs:
         if: matrix.version.name != 'stable'
         run: |
           cargo update -p=clap --precise=4.3.24
+          cargo update -p=clap_lex --precise=0.5.0
 
       - name: check minimal
         run: cargo ci-check-min
diff --git a/Cargo.toml b/Cargo.toml
index 65e3c6ae8..58fd96935 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,11 @@ members = [
     "awc",
 ]
 
+[workspace.package]
+license = "MIT OR Apache-2.0"
+edition = "2021"
+rust-version = "1.68"
+
 [profile.dev]
 # Disabling debug info speeds up builds a bunch and we don't rely on it for debugging that much.
 debug = 0
diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md
index 0dedd2c74..7078adb7e 100644
--- a/actix-http/CHANGES.md
+++ b/actix-http/CHANGES.md
@@ -4,6 +4,8 @@
 
 ### Added
 
+- Add `rustls-0_20` crate feature.
+- Add `{H1Service, H2Service, HttpService}::rustls_021()` and `HttpService::rustls_021_with_config()` service constructors.
 - Add `body::to_body_limit()` function.
 - Add `body::BodyLimitExceeded` error type.
 
diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml
index 6909b785f..61fba4bce 100644
--- a/actix-http/Cargo.toml
+++ b/actix-http/Cargo.toml
@@ -15,8 +15,9 @@ categories = [
     "web-programming::http-server",
     "web-programming::websocket",
 ]
-license = "MIT OR Apache-2.0"
-edition = "2021"
+license.workspace = true
+edition.workspace = true
+rust-version.workspace = true
 
 [package.metadata.docs.rs]
 # features that docs.rs will build with
@@ -43,15 +44,21 @@ ws = [
 # TLS via OpenSSL
 openssl = ["actix-tls/accept", "actix-tls/openssl"]
 
-# TLS via Rustls
-rustls = ["actix-tls/accept", "actix-tls/rustls"]
+# TLS via Rustls v0.20
+rustls = ["rustls-0_20"]
+
+# TLS via Rustls v0.20
+rustls-0_20 = ["actix-tls/accept", "actix-tls/rustls-0_20"]
+
+# TLS via Rustls v0.21
+rustls-0_21 = ["actix-tls/accept", "actix-tls/rustls-0_21"]
 
 # Compression codecs
 compress-brotli = ["__compress", "brotli"]
 compress-gzip   = ["__compress", "flate2"]
 compress-zstd   = ["__compress", "zstd"]
 
-# Internal (PRIVATE!) features used to aid testing and cheking feature status.
+# Internal (PRIVATE!) features used to aid testing and checking feature status.
 # Don't rely on these whatsoever. They are semver-exempt and may disappear at anytime.
 __compress = []
 
@@ -91,7 +98,7 @@ rand = { version = "0.8", optional = true }
 sha1 = { version = "0.10", optional = true }
 
 # openssl/rustls
-actix-tls = { version = "3", default-features = false, optional = true }
+actix-tls = { version = "3.1", default-features = false, optional = true }
 
 # compress-*
 brotli = { version = "3.3.3", optional = true }
@@ -101,7 +108,7 @@ zstd = { version = "0.12", optional = true }
 [dev-dependencies]
 actix-http-test = { version = "3", features = ["openssl"] }
 actix-server = "2"
-actix-tls = { version = "3", features = ["openssl"] }
+actix-tls = { version = "3.1", features = ["openssl"] }
 actix-web = "4"
 
 async-stream = "0.3"
@@ -118,12 +125,16 @@ serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 static_assertions = "1"
 tls-openssl = { package = "openssl", version = "0.10.55" }
-tls-rustls = { package = "rustls", version = "0.20" }
+tls-rustls_021 = { package = "rustls", version = "0.21" }
 tokio = { version = "1.24.2", features = ["net", "rt", "macros"] }
 
 [[example]]
 name = "ws"
-required-features = ["ws", "rustls"]
+required-features = ["ws", "rustls-0_21"]
+
+[[example]]
+name = "tls_rustls"
+required-features = ["http2", "rustls-0_21"]
 
 [[bench]]
 name = "response-body-compression"
diff --git a/actix-http/examples/tls_rustls.rs b/actix-http/examples/tls_rustls.rs
new file mode 100644
index 000000000..fbb55c6a4
--- /dev/null
+++ b/actix-http/examples/tls_rustls.rs
@@ -0,0 +1,73 @@
+//! Demonstrates TLS configuration (via Rustls) for HTTP/1.1 and HTTP/2 connections.
+//!
+//! Test using cURL:
+//!
+//! ```console
+//! $ curl --insecure https://127.0.0.1:8443
+//! Hello World!
+//! Protocol: HTTP/2.0
+//!
+//! $ curl --insecure --http1.1 https://127.0.0.1:8443
+//! Hello World!
+//! Protocol: HTTP/1.1
+//! ```
+
+extern crate tls_rustls_021 as rustls;
+
+use std::io;
+
+use actix_http::{Error, HttpService, Request, Response};
+use actix_utils::future::ok;
+
+#[actix_rt::main]
+async fn main() -> io::Result<()> {
+    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
+
+    tracing::info!("starting HTTP server at https://127.0.0.1:8443");
+
+    actix_server::Server::build()
+        .bind("echo", ("127.0.0.1", 8443), || {
+            HttpService::build()
+                .finish(|req: Request| {
+                    let body = format!(
+                        "Hello World!\n\
+                        Protocol: {:?}",
+                        req.head().version
+                    );
+                    ok::<_, Error>(Response::ok().set_body(body))
+                })
+                .rustls_021(rustls_config())
+        })?
+        .run()
+        .await
+}
+
+fn rustls_config() -> rustls::ServerConfig {
+    let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
+    let cert_file = cert.serialize_pem().unwrap();
+    let key_file = cert.serialize_private_key_pem();
+
+    let cert_file = &mut io::BufReader::new(cert_file.as_bytes());
+    let key_file = &mut io::BufReader::new(key_file.as_bytes());
+
+    let cert_chain = rustls_pemfile::certs(cert_file)
+        .unwrap()
+        .into_iter()
+        .map(rustls::Certificate)
+        .collect();
+    let mut keys = rustls_pemfile::pkcs8_private_keys(key_file).unwrap();
+
+    let mut config = rustls::ServerConfig::builder()
+        .with_safe_defaults()
+        .with_no_client_auth()
+        .with_single_cert(cert_chain, rustls::PrivateKey(keys.remove(0)))
+        .unwrap();
+
+    const H1_ALPN: &[u8] = b"http/1.1";
+    const H2_ALPN: &[u8] = b"h2";
+
+    config.alpn_protocols.push(H2_ALPN.to_vec());
+    config.alpn_protocols.push(H1_ALPN.to_vec());
+
+    config
+}
diff --git a/actix-http/examples/ws.rs b/actix-http/examples/ws.rs
index 6af6d5095..241175ae2 100644
--- a/actix-http/examples/ws.rs
+++ b/actix-http/examples/ws.rs
@@ -1,7 +1,7 @@
 //! Sets up a WebSocket server over TCP and TLS.
 //! Sends a heartbeat message every 4 seconds but does not respond to any incoming frames.
 
-extern crate tls_rustls as rustls;
+extern crate tls_rustls_021 as rustls;
 
 use std::{
     io,
@@ -28,7 +28,9 @@ async fn main() -> io::Result<()> {
             HttpService::build().h1(handler).tcp()
         })?
         .bind("tls", ("127.0.0.1", 8443), || {
-            HttpService::build().finish(handler).rustls(tls_config())
+            HttpService::build()
+                .finish(handler)
+                .rustls_021(tls_config())
         })?
         .run()
         .await
diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs
index fbda7138e..3b27e3db5 100644
--- a/actix-http/src/h1/service.rs
+++ b/actix-http/src/h1/service.rs
@@ -152,13 +152,13 @@ mod openssl {
     }
 }
 
-#[cfg(feature = "rustls")]
-mod rustls {
+#[cfg(feature = "rustls-0_20")]
+mod rustls_020 {
     use std::io;
 
     use actix_service::ServiceFactoryExt as _;
     use actix_tls::accept::{
-        rustls::{reexports::ServerConfig, Acceptor, TlsStream},
+        rustls_0_20::{reexports::ServerConfig, Acceptor, TlsStream},
         TlsError,
     };
 
@@ -188,7 +188,7 @@ mod rustls {
         U::Error: fmt::Display + Into<Response<BoxBody>>,
         U::InitError: fmt::Debug,
     {
-        /// Create Rustls based service.
+        /// Create Rustls v0.20 based service.
         pub fn rustls(
             self,
             config: ServerConfig,
@@ -213,6 +213,67 @@ mod rustls {
     }
 }
 
+#[cfg(feature = "rustls-0_21")]
+mod rustls_021 {
+    use std::io;
+
+    use actix_service::ServiceFactoryExt as _;
+    use actix_tls::accept::{
+        rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},
+        TlsError,
+    };
+
+    use super::*;
+
+    impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
+    where
+        S: ServiceFactory<Request, Config = ()>,
+        S::Future: 'static,
+        S::Error: Into<Response<BoxBody>>,
+        S::InitError: fmt::Debug,
+        S::Response: Into<Response<B>>,
+
+        B: MessageBody,
+
+        X: ServiceFactory<Request, Config = (), Response = Request>,
+        X::Future: 'static,
+        X::Error: Into<Response<BoxBody>>,
+        X::InitError: fmt::Debug,
+
+        U: ServiceFactory<
+            (Request, Framed<TlsStream<TcpStream>, Codec>),
+            Config = (),
+            Response = (),
+        >,
+        U::Future: 'static,
+        U::Error: fmt::Display + Into<Response<BoxBody>>,
+        U::InitError: fmt::Debug,
+    {
+        /// Create Rustls v0.21 based service.
+        pub fn rustls_021(
+            self,
+            config: ServerConfig,
+        ) -> impl ServiceFactory<
+            TcpStream,
+            Config = (),
+            Response = (),
+            Error = TlsError<io::Error, DispatchError>,
+            InitError = (),
+        > {
+            Acceptor::new(config)
+                .map_init_err(|_| {
+                    unreachable!("TLS acceptor service factory does not error on init")
+                })
+                .map_err(TlsError::into_service_error)
+                .map(|io: TlsStream<TcpStream>| {
+                    let peer_addr = io.get_ref().0.peer_addr().ok();
+                    (io, peer_addr)
+                })
+                .and_then(self.map_err(TlsError::Service))
+        }
+    }
+}
+
 impl<T, S, B, X, U> H1Service<T, S, B, X, U>
 where
     S: ServiceFactory<Request, Config = ()>,
diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs
index 3f742135a..0ae7ea682 100644
--- a/actix-http/src/h2/service.rs
+++ b/actix-http/src/h2/service.rs
@@ -140,8 +140,8 @@ mod openssl {
     }
 }
 
-#[cfg(feature = "rustls")]
-mod rustls {
+#[cfg(feature = "rustls-0_20")]
+mod rustls_020 {
     use std::io;
 
     use actix_service::ServiceFactoryExt as _;
@@ -162,7 +162,7 @@ mod rustls {
 
         B: MessageBody + 'static,
     {
-        /// Create Rustls based service.
+        /// Create Rustls v0.20 based service.
         pub fn rustls(
             self,
             mut config: ServerConfig,
@@ -191,6 +191,57 @@ mod rustls {
     }
 }
 
+#[cfg(feature = "rustls-0_21")]
+mod rustls_021 {
+    use std::io;
+
+    use actix_service::ServiceFactoryExt as _;
+    use actix_tls::accept::{
+        rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},
+        TlsError,
+    };
+
+    use super::*;
+
+    impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
+    where
+        S: ServiceFactory<Request, Config = ()>,
+        S::Future: 'static,
+        S::Error: Into<Response<BoxBody>> + 'static,
+        S::Response: Into<Response<B>> + 'static,
+        <S::Service as Service<Request>>::Future: 'static,
+
+        B: MessageBody + 'static,
+    {
+        /// Create Rustls v0.21 based service.
+        pub fn rustls_021(
+            self,
+            mut config: ServerConfig,
+        ) -> impl ServiceFactory<
+            TcpStream,
+            Config = (),
+            Response = (),
+            Error = TlsError<io::Error, DispatchError>,
+            InitError = S::InitError,
+        > {
+            let mut protos = vec![b"h2".to_vec()];
+            protos.extend_from_slice(&config.alpn_protocols);
+            config.alpn_protocols = protos;
+
+            Acceptor::new(config)
+                .map_init_err(|_| {
+                    unreachable!("TLS acceptor service factory does not error on init")
+                })
+                .map_err(TlsError::into_service_error)
+                .map(|io: TlsStream<TcpStream>| {
+                    let peer_addr = io.get_ref().0.peer_addr().ok();
+                    (io, peer_addr)
+                })
+                .and_then(self.map_err(TlsError::Service))
+        }
+    }
+}
+
 impl<T, S, B> ServiceFactory<(T, Option<net::SocketAddr>)> for H2Service<T, S, B>
 where
     T: AsyncRead + AsyncWrite + Unpin + 'static,
diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs
index 8b755f2f4..382295fbc 100644
--- a/actix-http/src/lib.rs
+++ b/actix-http/src/lib.rs
@@ -58,7 +58,7 @@ pub mod ws;
 
 #[allow(deprecated)]
 pub use self::payload::PayloadStream;
-#[cfg(any(feature = "openssl", feature = "rustls"))]
+#[cfg(any(feature = "openssl", feature = "rustls-0_20", feature = "rustls-0_21"))]
 pub use self::service::TlsAcceptorConfig;
 pub use self::{
     builder::HttpServiceBuilder,
diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs
index e118d8361..bb0fda8c4 100644
--- a/actix-http/src/service.rs
+++ b/actix-http/src/service.rs
@@ -241,13 +241,13 @@ where
 }
 
 /// Configuration options used when accepting TLS connection.
-#[cfg(any(feature = "openssl", feature = "rustls"))]
+#[cfg(any(feature = "openssl", feature = "rustls-0_20", feature = "rustls-0_21"))]
 #[derive(Debug, Default)]
 pub struct TlsAcceptorConfig {
     pub(crate) handshake_timeout: Option<std::time::Duration>,
 }
 
-#[cfg(any(feature = "openssl", feature = "rustls"))]
+#[cfg(any(feature = "openssl", feature = "rustls", feature = "rustls-0_21"))]
 impl TlsAcceptorConfig {
     /// Set TLS handshake timeout duration.
     pub fn handshake_timeout(self, dur: std::time::Duration) -> Self {
@@ -352,8 +352,8 @@ mod openssl {
     }
 }
 
-#[cfg(feature = "rustls")]
-mod rustls {
+#[cfg(feature = "rustls-0_20")]
+mod rustls_020 {
     use std::io;
 
     use actix_service::ServiceFactoryExt as _;
@@ -448,6 +448,102 @@ mod rustls {
     }
 }
 
+#[cfg(feature = "rustls-0_21")]
+mod rustls_021 {
+    use std::io;
+
+    use actix_service::ServiceFactoryExt as _;
+    use actix_tls::accept::{
+        rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},
+        TlsError,
+    };
+
+    use super::*;
+
+    impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
+    where
+        S: ServiceFactory<Request, Config = ()>,
+        S::Future: 'static,
+        S::Error: Into<Response<BoxBody>> + 'static,
+        S::InitError: fmt::Debug,
+        S::Response: Into<Response<B>> + 'static,
+        <S::Service as Service<Request>>::Future: 'static,
+
+        B: MessageBody + 'static,
+
+        X: ServiceFactory<Request, Config = (), Response = Request>,
+        X::Future: 'static,
+        X::Error: Into<Response<BoxBody>>,
+        X::InitError: fmt::Debug,
+
+        U: ServiceFactory<
+            (Request, Framed<TlsStream<TcpStream>, h1::Codec>),
+            Config = (),
+            Response = (),
+        >,
+        U::Future: 'static,
+        U::Error: fmt::Display + Into<Response<BoxBody>>,
+        U::InitError: fmt::Debug,
+    {
+        /// Create Rustls based service.
+        pub fn rustls_021(
+            self,
+            config: ServerConfig,
+        ) -> impl ServiceFactory<
+            TcpStream,
+            Config = (),
+            Response = (),
+            Error = TlsError<io::Error, DispatchError>,
+            InitError = (),
+        > {
+            self.rustls_021_with_config(config, TlsAcceptorConfig::default())
+        }
+
+        /// Create Rustls based service with custom TLS acceptor configuration.
+        pub fn rustls_021_with_config(
+            self,
+            mut config: ServerConfig,
+            tls_acceptor_config: TlsAcceptorConfig,
+        ) -> impl ServiceFactory<
+            TcpStream,
+            Config = (),
+            Response = (),
+            Error = TlsError<io::Error, DispatchError>,
+            InitError = (),
+        > {
+            let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
+            protos.extend_from_slice(&config.alpn_protocols);
+            config.alpn_protocols = protos;
+
+            let mut acceptor = Acceptor::new(config);
+
+            if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {
+                acceptor.set_handshake_timeout(handshake_timeout);
+            }
+
+            acceptor
+                .map_init_err(|_| {
+                    unreachable!("TLS acceptor service factory does not error on init")
+                })
+                .map_err(TlsError::into_service_error)
+                .and_then(|io: TlsStream<TcpStream>| async {
+                    let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {
+                        if protos.windows(2).any(|window| window == b"h2") {
+                            Protocol::Http2
+                        } else {
+                            Protocol::Http1
+                        }
+                    } else {
+                        Protocol::Http1
+                    };
+                    let peer_addr = io.get_ref().0.peer_addr().ok();
+                    Ok((io, proto, peer_addr))
+                })
+                .and_then(self.map_err(TlsError::Service))
+        }
+    }
+}
+
 impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>
     for HttpService<T, S, B, X, U>
 where
diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs
index 3d9a39cbd..c94e579e5 100644
--- a/actix-http/tests/test_rustls.rs
+++ b/actix-http/tests/test_rustls.rs
@@ -1,7 +1,6 @@
-#![cfg(feature = "rustls")]
-#![allow(clippy::uninlined_format_args)]
+#![cfg(feature = "rustls-0_21")]
 
-extern crate tls_rustls as rustls;
+extern crate tls_rustls_021 as rustls;
 
 use std::{
     convert::Infallible,
@@ -21,7 +20,7 @@ use actix_http::{
 use actix_http_test::test_server;
 use actix_rt::pin;
 use actix_service::{fn_factory_with_config, fn_service};
-use actix_tls::connect::rustls::webpki_roots_cert_store;
+use actix_tls::connect::rustls_0_21::webpki_roots_cert_store;
 use actix_utils::future::{err, ok, poll_fn};
 use bytes::{Bytes, BytesMut};
 use derive_more::{Display, Error};
@@ -110,7 +109,7 @@ async fn h1() -> io::Result<()> {
     let srv = test_server(move || {
         HttpService::build()
             .h1(|_| ok::<_, Error>(Response::ok()))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -124,7 +123,7 @@ async fn h2() -> io::Result<()> {
     let srv = test_server(move || {
         HttpService::build()
             .h2(|_| ok::<_, Error>(Response::ok()))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -142,7 +141,7 @@ async fn h1_1() -> io::Result<()> {
                 assert_eq!(req.version(), Version::HTTP_11);
                 ok::<_, Error>(Response::ok())
             })
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -160,7 +159,7 @@ async fn h2_1() -> io::Result<()> {
                 assert_eq!(req.version(), Version::HTTP_2);
                 ok::<_, Error>(Response::ok())
             })
-            .rustls_with_config(
+            .rustls_021_with_config(
                 tls_config(),
                 TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5)),
             )
@@ -181,7 +180,7 @@ async fn h2_body1() -> io::Result<()> {
                 let body = load_body(req.take_payload()).await?;
                 Ok::<_, Error>(Response::ok().set_body(body))
             })
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -207,7 +206,7 @@ async fn h2_content_length() {
                 ];
                 ok::<_, Infallible>(Response::new(statuses[indx]))
             })
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -279,7 +278,7 @@ async fn h2_headers() {
                 }
                 ok::<_, Infallible>(config.body(data.clone()))
             })
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -318,7 +317,7 @@ async fn h2_body2() {
     let mut srv = test_server(move || {
         HttpService::build()
             .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -335,7 +334,7 @@ async fn h2_head_empty() {
     let mut srv = test_server(move || {
         HttpService::build()
             .finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -361,7 +360,7 @@ async fn h2_head_binary() {
     let mut srv = test_server(move || {
         HttpService::build()
             .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -386,7 +385,7 @@ async fn h2_head_binary2() {
     let srv = test_server(move || {
         HttpService::build()
             .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -412,7 +411,7 @@ async fn h2_body_length() {
                     Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),
                 )
             })
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -436,7 +435,7 @@ async fn h2_body_chunked_explicit() {
                         .body(BodyStream::new(body)),
                 )
             })
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -465,7 +464,7 @@ async fn h2_response_http_error_handling() {
                     )
                 }))
             }))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -495,7 +494,7 @@ async fn h2_service_error() {
     let mut srv = test_server(move || {
         HttpService::build()
             .h2(|_| err::<Response<BoxBody>, _>(BadRequest))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -512,7 +511,7 @@ async fn h1_service_error() {
     let mut srv = test_server(move || {
         HttpService::build()
             .h1(|_| err::<Response<BoxBody>, _>(BadRequest))
-            .rustls(tls_config())
+            .rustls_021(tls_config())
     })
     .await;
 
@@ -535,7 +534,7 @@ async fn alpn_h1() -> io::Result<()> {
         config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
         HttpService::build()
             .h1(|_| ok::<_, Error>(Response::ok()))
-            .rustls(config)
+            .rustls_021(config)
     })
     .await;
 
@@ -557,7 +556,7 @@ async fn alpn_h2() -> io::Result<()> {
         config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
         HttpService::build()
             .h2(|_| ok::<_, Error>(Response::ok()))
-            .rustls(config)
+            .rustls_021(config)
     })
     .await;
 
@@ -583,7 +582,7 @@ async fn alpn_h2_1() -> io::Result<()> {
         config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
         HttpService::build()
             .finish(|_| ok::<_, Error>(Response::ok()))
-            .rustls(config)
+            .rustls_021(config)
     })
     .await;
 
diff --git a/actix-web/Cargo.toml b/actix-web/Cargo.toml
index 4322fb871..7dbaa2a29 100644
--- a/actix-web/Cargo.toml
+++ b/actix-web/Cargo.toml
@@ -16,7 +16,8 @@ categories = [
 homepage = "https://actix.rs"
 repository = "https://github.com/actix/actix-web.git"
 license = "MIT OR Apache-2.0"
-edition = "2021"
+edition.workspace = true
+rust-version.workspace = true
 
 [package.metadata.docs.rs]
 # features that docs.rs will build with
diff --git a/awc/Cargo.toml b/awc/Cargo.toml
index daec84ab9..2a09a52c4 100644
--- a/awc/Cargo.toml
+++ b/awc/Cargo.toml
@@ -45,7 +45,7 @@ cookies = ["cookie"]
 # trust-dns as dns resolver
 trust-dns = ["trust-dns-resolver"]
 
-# Internal (PRIVATE!) features used to aid testing and cheking feature status.
+# Internal (PRIVATE!) features used to aid testing and checking feature status.
 # Don't rely on these whatsoever. They may disappear at anytime.
 __compress = []