diff --git a/src/main/java/com/sparrowwallet/sparrow/AppServices.java b/src/main/java/com/sparrowwallet/sparrow/AppServices.java
index 580fea57..2472c569 100644
--- a/src/main/java/com/sparrowwallet/sparrow/AppServices.java
+++ b/src/main/java/com/sparrowwallet/sparrow/AppServices.java
@@ -46,10 +46,7 @@ import java.awt.desktop.OpenFilesHandler;
 import java.awt.desktop.OpenURIHandler;
 import java.io.File;
 import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.net.*;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.temporal.ChronoUnit;
@@ -69,6 +66,7 @@ public class AppServices {
     private static final int VERSION_CHECK_PERIOD_HOURS = 24;
     private static final ExchangeSource DEFAULT_EXCHANGE_SOURCE = ExchangeSource.COINGECKO;
     private static final Currency DEFAULT_FIAT_CURRENCY = Currency.getInstance("USD");
+    private static final String TOR_DEFAULT_PROXY_CIRCUIT_ID = "default";
 
     private static AppServices INSTANCE;
 
@@ -375,17 +373,31 @@ public class AppServices {
     }
 
     public static Proxy getProxy() {
+        return getProxy(TOR_DEFAULT_PROXY_CIRCUIT_ID);
+    }
+
+    public static Proxy getProxy(String proxyCircuitId) {
         Config config = Config.get();
+        Proxy proxy = null;
         if(config.isUseProxy()) {
-            HostAndPort proxy = HostAndPort.fromString(config.getProxyServer());
-            InetSocketAddress proxyAddress = new InetSocketAddress(proxy.getHost(), proxy.getPortOrDefault(ProxyTcpOverTlsTransport.DEFAULT_PROXY_PORT));
-            return new Proxy(Proxy.Type.SOCKS, proxyAddress);
+            HostAndPort proxyHostAndPort = HostAndPort.fromString(config.getProxyServer());
+            InetSocketAddress proxyAddress = new InetSocketAddress(proxyHostAndPort.getHost(), proxyHostAndPort.getPortOrDefault(ProxyTcpOverTlsTransport.DEFAULT_PROXY_PORT));
+            proxy = new Proxy(Proxy.Type.SOCKS, proxyAddress);
         } else if(AppServices.isTorRunning()) {
             InetSocketAddress proxyAddress = new InetSocketAddress("localhost", TorService.PROXY_PORT);
-            return new Proxy(Proxy.Type.SOCKS, proxyAddress);
+            proxy = new Proxy(Proxy.Type.SOCKS, proxyAddress);
+        }
+
+        //Setting new proxy authentication credentials will force a new Tor circuit to be created
+        if(proxy != null) {
+            Authenticator.setDefault(new Authenticator() {
+                public PasswordAuthentication getPasswordAuthentication() {
+                    return (new PasswordAuthentication("user", proxyCircuitId.toCharArray()));
+                }
+            });
         }
 
-        return null;
+        return proxy;
     }
 
     static void initialize(MainApp application) {
diff --git a/src/main/java/com/sparrowwallet/sparrow/net/BroadcastSource.java b/src/main/java/com/sparrowwallet/sparrow/net/BroadcastSource.java
new file mode 100644
index 00000000..a376ed46
--- /dev/null
+++ b/src/main/java/com/sparrowwallet/sparrow/net/BroadcastSource.java
@@ -0,0 +1,137 @@
+package com.sparrowwallet.sparrow.net;
+
+import com.sparrowwallet.drongo.Network;
+import com.sparrowwallet.drongo.Utils;
+import com.sparrowwallet.drongo.protocol.Sha256Hash;
+import com.sparrowwallet.drongo.protocol.Transaction;
+import com.sparrowwallet.sparrow.AppServices;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+
+public enum BroadcastSource {
+    BLOCKSTREAM_INFO("blockstream.info", "https://blockstream.info", "http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion") {
+        @Override
+        public Sha256Hash broadcastTransaction(Transaction transaction) throws BroadcastException {
+            String data = Utils.bytesToHex(transaction.bitcoinSerialize());
+            return postTransactionData(data);
+        }
+
+        protected URL getURL(Proxy proxy) throws MalformedURLException {
+            if(Network.get() == Network.MAINNET) {
+                return new URL(getBaseUrl(proxy) + "/api/tx");
+            } else if(Network.get() == Network.TESTNET) {
+                return new URL(getBaseUrl(proxy) + "/testnet/api/tx");
+            } else {
+                throw new IllegalStateException("Cannot broadcast transaction to " + getName() + " on network " + Network.get());
+            }
+        }
+    },
+    MEMPOOL_SPACE("mempool.space", "https://mempool.space", "http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion") {
+        public Sha256Hash broadcastTransaction(Transaction transaction) throws BroadcastException {
+            String data = Utils.bytesToHex(transaction.bitcoinSerialize());
+            return postTransactionData(data);
+        }
+
+        protected URL getURL(Proxy proxy) throws MalformedURLException {
+            if(Network.get() == Network.MAINNET) {
+                return new URL(getBaseUrl(proxy) + "/api/tx");
+            } else if(Network.get() == Network.TESTNET) {
+                return new URL(getBaseUrl(proxy) + "/testnet/api/tx");
+            } else {
+                throw new IllegalStateException("Cannot broadcast transaction to " + getName() + " on network " + Network.get());
+            }
+        }
+    };
+
+    private final String name;
+    private final String tlsUrl;
+    private final String onionUrl;
+
+    private static final Logger log = LoggerFactory.getLogger(BroadcastSource.class);
+    private static final SecureRandom secureRandom = new SecureRandom();
+
+    BroadcastSource(String name, String tlsUrl, String onionUrl) {
+        this.name = name;
+        this.tlsUrl = tlsUrl;
+        this.onionUrl = onionUrl;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getTlsUrl() {
+        return tlsUrl;
+    }
+
+    public String getOnionUrl() {
+        return onionUrl;
+    }
+
+    public String getBaseUrl(Proxy proxy) {
+        return (proxy == null ? getTlsUrl() : getOnionUrl());
+    }
+
+    public abstract Sha256Hash broadcastTransaction(Transaction transaction) throws BroadcastException;
+
+    protected abstract URL getURL(Proxy proxy) throws MalformedURLException;
+
+    public Sha256Hash postTransactionData(String data) throws BroadcastException {
+        //If a Tor proxy is configured, ensure we use a new circuit by configuring a random proxy password
+        Proxy proxy = AppServices.getProxy(Integer.toString(secureRandom.nextInt()));
+
+        try {
+            URL url = getURL(proxy);
+
+            HttpURLConnection connection = proxy == null ? (HttpURLConnection)url.openConnection() : (HttpURLConnection)url.openConnection(proxy);
+            connection.setRequestMethod("POST");
+            connection.setRequestProperty("Content-Type", "text/plain");
+            connection.setDoOutput(true);
+
+            try(OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream())) {
+                writer.write(data);
+                writer.flush();
+            }
+
+            StringBuilder response = new StringBuilder();
+            try(BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
+                String responseLine;
+                while((responseLine = br.readLine()) != null) {
+                    response.append(responseLine.trim());
+                }
+            }
+
+            int statusCode = connection.getResponseCode();
+            if(statusCode < 200 || statusCode >= 300) {
+                throw new BroadcastException("Could not broadcast transaction, server returned " + statusCode + ": " + response);
+            }
+
+            try {
+                return Sha256Hash.wrap(response.toString().trim());
+            } catch(Exception e) {
+                throw new BroadcastException("Could not retrieve txid from broadcast, server returned " + statusCode + ": " + response);
+            }
+        } catch(IOException e) {
+            log.error("Could not post transaction via " + getName(), e);
+            throw new BroadcastException("Could not broadcast transaction via " + getName(), e);
+        }
+    }
+
+    public static final class BroadcastException extends Exception {
+        public BroadcastException(String message) {
+            super(message);
+        }
+
+        public BroadcastException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+}
diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java
index 75be18c2..11e86b98 100644
--- a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java
+++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java
@@ -1249,6 +1249,19 @@ public class ElectrumServer {
         protected Task<Sha256Hash> createTask() {
             return new Task<>() {
                 protected Sha256Hash call() throws ServerException {
+                    //If Tor proxy is configured, try all external broadcast sources in random order before falling back to connected Electrum server
+                    if(AppServices.getProxy() != null) {
+                        List<BroadcastSource> broadcastSources = new ArrayList<>(Arrays.asList(BroadcastSource.values()));
+                        while(!broadcastSources.isEmpty()) {
+                            try {
+                                BroadcastSource broadcastSource = broadcastSources.remove(new Random().nextInt(broadcastSources.size()));
+                                return broadcastSource.broadcastTransaction(transaction);
+                            } catch(BroadcastSource.BroadcastException e) {
+                                //ignore, already logged
+                            }
+                        }
+                    }
+
                     ElectrumServer electrumServer = new ElectrumServer();
                     return electrumServer.broadcastTransaction(transaction);
                 }