diff --git a/Cargo.lock b/Cargo.lock
index 59093f03a7031b9a77b234e09a5915dc0ef6483f..e3d76404c2d3bfb83632a079ab1e8c0e4e5d2a5a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -189,6 +189,23 @@ dependencies = [
  "tower-service",
 ]
 
+[[package]]
+name = "axum-server-dual-protocol"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1a8f5076b5dbfeb706bcce30fe73caf20971e6e5ca80b83a7f1d990e73e185"
+dependencies = [
+ "axum-server",
+ "bytes",
+ "http",
+ "hyper",
+ "pin-project",
+ "tokio",
+ "tokio-rustls",
+ "tokio-util",
+ "tower-layer",
+]
+
 [[package]]
 name = "backtrace"
 version = "0.3.69"
@@ -394,6 +411,7 @@ dependencies = [
  "async-trait",
  "axum",
  "axum-server",
+ "axum-server-dual-protocol",
  "base64",
  "bytes",
  "clap",
@@ -444,6 +462,7 @@ dependencies = [
  "tracing-opentelemetry",
  "tracing-subscriber",
  "trust-dns-resolver",
+ "urlencoding",
  "webpage",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index ce37ee71e1531cbb2861ef6362408e0581e8eac7..30e734f4df35e9994746cdc3161e2a85d031fa92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -108,6 +108,12 @@ rocksdb = { version = "0.22.0", default-features = true, features = ["multi-thre
 
 either = { version = "1.10.0", features = ["serde"] }
 
+# to listen on both HTTP and HTTPS
+axum-server-dual-protocol = { version = "0.5.2", optional = true }
+
+# to encode/decode percent URIs when conduwuit is running without a reverse proxy
+urlencoding = "2.1.3"
+
 
 [target.'cfg(unix)'.dependencies]
 nix = { version = "0.28.0", features = ["resource"] }
@@ -126,6 +132,7 @@ zstd_compression = []
 #compression = ["tower-http/compression-full"]
 sha256_media = []
 io_uring = ["rocksdb/io-uring"]
+axum_dual_protocol = ["axum-server-dual-protocol"]
 
 [[bin]]
 name = "conduit"
diff --git a/complement/Dockerfile b/complement/Dockerfile
index a452b09d232ac2afb95ed34ef4d43f0fb88b81d5..e168b1b8ced5961f2d6f6ccd21c221775a4ee3cb 100644
--- a/complement/Dockerfile
+++ b/complement/Dockerfile
@@ -8,31 +8,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
 COPY Cargo.toml Cargo.toml
 COPY Cargo.lock Cargo.lock
 COPY src src
-RUN cargo build --release \
+RUN cargo build --release --features=axum_dual_protocol \
     && mv target/release/conduit conduit \
     && rm -rf target
 
-# Install caddy
-RUN apt-get update \
-    && apt-get install -y \
-    debian-keyring \
-    debian-archive-keyring \
-    apt-transport-https \
-    curl \
-    && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/gpg.key' \
-    | gpg --dearmor -o /usr/share/keyrings/caddy-testing-archive-keyring.gpg \
-    && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/debian.deb.txt' \
-    | tee /etc/apt/sources.list.d/caddy-testing.list \
-    && apt-get update \
-    && apt-get install -y caddy
-
 COPY conduwuit-example.toml conduit.toml
-COPY complement/caddy.json caddy.json
 
 ENV SERVER_NAME=localhost
 ENV CONDUIT_CONFIG=/workdir/conduit.toml
 
-RUN sed -i "s/port = 6167/port = 8008/g" conduit.toml
+RUN sed -i "s/port = 6167/port = [8448, 8008]/g" conduit.toml
 RUN sed -i "s/allow_registration = false/allow_registration = true/g" conduit.toml
 RUN sed -i "s/registration_token/#registration_token/g" conduit.toml
 RUN sed -i "s/allow_guest_registration = false/allow_guest_registration = true/g" conduit.toml
@@ -41,22 +26,39 @@ RUN sed -i "s/allow_public_room_directory_without_auth = false/allow_public_room
 RUN sed -i "s/allow_device_name_federation = false/allow_device_name_federation = true/g" conduit.toml
 RUN sed -i "/\"127.0.0.0/d" conduit.toml
 RUN sed -i "/\"10.0.0.0/d" conduit.toml
+RUN sed -i "/\"172.16.0.0/d" conduit.toml
 RUN sed -i "/\"::1/d" conduit.toml
-RUN echo "log = \"warn\"" >> conduit.toml
-RUN echo 'yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true' >> conduit.toml
-RUN echo 'allow_outgoing_presence = true' >> conduit.toml
-RUN echo 'allow_incoming_presence = true' >> conduit.toml
-RUN echo 'allow_local_presence = true' >> conduit.toml
+RUN sed -i "s/#log = \"warn\"/log = \"debug\"/g" conduit.toml
+RUN sed -i 's/#\strusted_servers\s=\s\["matrix.org"\]/trusted_servers = []/g' conduit.toml
+RUN sed -i 's/# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` to/yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true/g' conduit.toml
+RUN sed -i "s/allow_outgoing_presence = false/allow_outgoing_presence = true/g" conduit.toml
+RUN sed -i "s/allow_incoming_presence = false/allow_incoming_presence = true/g" conduit.toml
+RUN sed -i "s/allow_local_presence = false/allow_local_presence = true/g" conduit.toml
 RUN sed -i "s/address = \"127.0.0.1\"/address = \"0.0.0.0\"/g" conduit.toml
-RUN echo '[global.tls]' >> conduit.toml
-RUN echo 'certs = "/complement/ca/ca.crt"' >> conduit.toml
-RUN echo 'key = "/complement/ca/ca.key"' >> conduit.toml
+
+# https://stackoverflow.com/questions/76049656/unexpected-notvalidforname-with-rusts-tonic-with-tls
+RUN echo "authorityKeyIdentifier=keyid,issuer" >> extensions.ext
+RUN echo "basicConstraints=CA:FALSE" >> extensions.ext
+RUN echo 'subjectAltName = @alt_names' >> extensions.ext
+RUN echo '[alt_names]' >> extensions.ext
+RUN echo "DNS.1 = servername" >> extensions.ext
+RUN echo "IP.1 = ipaddress" >> extensions.ext
+
 
 EXPOSE 8008 8448
 
 CMD uname -a && \
+    cp -f -v /complement/ca/ca.crt /usr/local/share/ca-certificates/complement.crt && \
+    update-ca-certificates && \
+    sed -i "s/servername/${SERVER_NAME}/g" extensions.ext && \
+    sed -i "s/ipaddress/`hostname -i`/g" extensions.ext && \
+    openssl req -newkey rsa:2048 -noenc -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=$SERVER_NAME" -keyout $SERVER_NAME.key -out $SERVER_NAME.csr && \
+    openssl x509 -signkey $SERVER_NAME.key -in $SERVER_NAME.csr -req -days 2 -out $SERVER_NAME.crt && \
+    openssl x509 -req -CA /complement/ca/ca.crt -CAkey /complement/ca/ca.key -in $SERVER_NAME.csr -out $SERVER_NAME.crt -days 2 -CAcreateserial -extfile extensions.ext && \
     sed -i "s/#server_name = \"your.server.name\"/server_name = \"${SERVER_NAME}\"/g" conduit.toml && \
-    sed -i "s/your.server.name/${SERVER_NAME}/g" caddy.json && \
-    caddy start --config caddy.json > /dev/null && \
+    sed -i 's/#\s\[global.tls\]/\[global.tls\]/g' conduit.toml && \
+    sed -i "s/# certs = \"\/path\/to\/my\/certificate.crt\"/certs = \"${SERVER_NAME}.crt\"/g" conduit.toml && \
+    sed -i "s/# key = \"\/path\/to\/my\/private_key.key\"/key = \"${SERVER_NAME}.key\"/g" conduit.toml && \
+    sed -i "s/#dual_protocol = false/dual_protocol = true/g" conduit.toml && \
     /workdir/conduit
 
diff --git a/complement/caddy.json b/complement/caddy.json
deleted file mode 100644
index ea52c2c98165b0cb1f733450722f1690ff470406..0000000000000000000000000000000000000000
--- a/complement/caddy.json
+++ /dev/null
@@ -1,72 +0,0 @@
-{
-    "logging": {
-        "logs": {
-            "default": {
-                "level": "WARN"
-            }
-        }
-    },
-    "apps": {
-        "http": {
-            "https_port": 8448,
-            "servers": {
-                "srv0": {
-                    "listen": [":8448"],
-                    "routes": [{
-                        "match": [{
-                            "host": ["your.server.name"]
-                        }],
-                        "handle": [{
-                            "handler": "subroute",
-                            "routes": [{
-                                "handle": [{
-                                    "handler": "reverse_proxy",
-                                    "upstreams": [{
-                                        "dial": "127.0.0.1:8008"
-                                    }]
-                                }]
-                            }]
-                        }],
-                        "terminal": true
-                    }],
-                    "tls_connection_policies": [{
-                        "match": {
-                            "sni": ["your.server.name"]
-                        }
-                    }]
-                }
-            }
-        },
-        "pki": {
-            "certificate_authorities": {
-                "local": {
-                    "name": "Complement CA",
-                    "root": {
-                        "certificate": "/complement/ca/ca.crt",
-                        "private_key": "/complement/ca/ca.key"
-                    },
-                    "intermediate": {
-                        "certificate": "/complement/ca/ca.crt",
-                        "private_key": "/complement/ca/ca.key"
-                    }
-                }
-            }
-        },
-        "tls": {
-            "automation": {
-                "policies": [{
-                    "subjects": ["your.server.name"],
-                    "issuers": [{
-                        "module": "internal"
-                    }],
-                    "on_demand": true
-                }, {
-                    "issuers": [{
-                        "module": "internal",
-                        "ca": "local"
-                    }]
-                }]
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/conduwuit-example.toml b/conduwuit-example.toml
index c5ec97b9ea554645e35f14d5937a9a16310cd0ff..829dbffa45e236b7de53ef5297f8a9523289de1c 100644
--- a/conduwuit-example.toml
+++ b/conduwuit-example.toml
@@ -287,4 +287,9 @@ allow_check_for_updates = true
 # It is strongly recommended you use a reverse proxy instead. This is primarily relevant for test suites like complement that require a private CA setup.
 # [global.tls]
 # certs = "/path/to/my/certificate.crt"
-# key = "/path/to/my/private_key.key"
\ No newline at end of file
+# key = "/path/to/my/private_key.key"
+#
+# Whether to listen and allow for HTTP and HTTPS connections (insecure!)
+# This config option is only available if conduwuit was built with `axum_dual_protocol` feature (not default feature)
+# Defaults to false
+#dual_protocol = false
\ No newline at end of file
diff --git a/src/config/mod.rs b/src/config/mod.rs
index b5e9002b4253e0a9a596fb2cf0858ba18a1f86de..604da5467ec78c7b76df7950c0e9ad0cf2cd8d13 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -170,6 +170,10 @@ pub struct Config {
 pub struct TlsConfig {
     pub certs: String,
     pub key: String,
+    #[serde(default)]
+    /// Whether to listen and allow for HTTP and HTTPS connections (insecure!)
+    /// Only works / does something if the `axum_dual_protocol` feature flag was built
+    pub dual_protocol: bool,
 }
 
 const DEPRECATED_KEYS: &[&str] = &["cache_capacity"];
diff --git a/src/main.rs b/src/main.rs
index 8aae73346414f74c0836ce86f3d11bab0c4d7774..d9fd1682d4f6716c487e94409e1a5220c50a90fd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -43,6 +43,9 @@
 
 use clap::Parser;
 
+#[cfg(feature = "axum_dual_protocol")]
+use axum_server_dual_protocol::ServerExt;
+
 pub use conduit::*; // Re-export everything from the library crate
 
 #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
@@ -265,9 +268,11 @@ async fn run_server() -> io::Result<()> {
     };
 
     let x_requested_with = HeaderName::from_static("x-requested-with");
+    let x_forwarded_for = HeaderName::from_static("x-forwarded-for");
 
     let middlewares = ServiceBuilder::new()
         .sensitive_headers([header::AUTHORIZATION])
+        .sensitive_request_headers([x_forwarded_for].into())
         .layer(axum::middleware::from_fn(spawn_task))
         .layer(
             TraceLayer::new_for_http()
@@ -365,24 +370,50 @@ async fn run_server() -> io::Result<()> {
                 );
                 info!("Note: It is strongly recommended that you use a reverse proxy instead of running conduwuit directly with TLS.");
                 let conf = RustlsConfig::from_pem_file(&tls.certs, &tls.key).await?;
-                debug!("Rustlsconfig: {:?}", conf);
 
-                let mut join_set = JoinSet::new();
-                for addr in &addrs {
-                    join_set.spawn(
-                        bind_rustls(*addr, conf.clone())
-                            .handle(handle.clone())
-                            .serve(app.clone()),
+                if cfg!(feature = "axum_dual_protocol") {
+                    info!(
+                        "conduwuit was built with axum_dual_protocol feature to listen on both HTTP and HTTPS. This will only take affect if `dual_protocol` is enabled in `[global.tls]`"
                     );
                 }
 
+                let mut join_set = JoinSet::new();
+
+                if cfg!(feature = "axum_dual_protocol") && tls.dual_protocol {
+                    #[cfg(feature = "axum_dual_protocol")]
+                    for addr in &addrs {
+                        join_set.spawn(
+                            axum_server_dual_protocol::bind_dual_protocol(*addr, conf.clone())
+                                .set_upgrade(false)
+                                .handle(handle.clone())
+                                .serve(app.clone()),
+                        );
+                    }
+                } else {
+                    for addr in &addrs {
+                        join_set.spawn(
+                            bind_rustls(*addr, conf.clone())
+                                .handle(handle.clone())
+                                .serve(app.clone()),
+                        );
+                    }
+                }
+
                 #[cfg(feature = "systemd")]
                 let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]);
 
-                info!(
-                    "Listening on {:?} with TLS certificates {}",
+                if cfg!(feature = "axum_dual_protocol") && tls.dual_protocol {
+                    warn!(
+                    "Listening on {:?} with TLS certificate {} and supporting plain text (HTTP) connections too (insecure!)",
                     addrs, &tls.certs
                 );
+                } else {
+                    info!(
+                        "Listening on {:?} with TLS certificate {}",
+                        addrs, &tls.certs
+                    );
+                }
+
                 join_set.join_next().await;
             }
             None => {