How to Bypass ISP Blocking and Use WireGuard Normally | WireGuard Unblock Solutions
How to Deal with WireGuard Being Blocked
Background
To securely access my home devices, I chose WireGuard for remote networking, combined with DDNS for great results. However, after some time, I often found that I could not connect. Changing the WireGuard port would immediately restore connectivity, indicating that the ISP had blocked the port.
Why Does Blocking Happen? (Speculative Principle)
During networking, I found that if no keepalive is configured and there is no access for a long time, connections may not be timely. So I configured a keepalive for the peer:
[Peer]
PersistentKeepalive = 25
If there are 10 to dozens of devices accessing via DDNS and sending regular keepalives to the WireGuard server, this forms a strong pattern, revealing that a background service may be running on this broadband device, which ISPs may mark as high risk.
Since this is domestic access, it likely does not involve the China GFW. It is more likely that some ISPs along the route (possibly due to inter-ISP traffic settlement issues) deliberately block certain UDP protocols that appear to be non-commercial services.
UDP is often used for aggressive, high-speed, PCDN, or live streaming scenarios. Normally, most people use TCP. This kind of high-cost, 'junk' UDP traffic may be counted and thus ISPs prevent home broadband from widely using UDP services.
Because the port offset in UDP and TCP packet headers is the same, it is easy to develop a traffic statistics program for this purpose.
Solutions
How to fight against this unreasonable blocking? We are not violating any rules, just doing remote networking. Steps are as follows:
Set Up Multiple Port Forwarding Rules to WireGuard on Your Home Router
- Suppose your WireGuard is exposed on port 51820; configure a batch of port forwarding rules, e.g., forward ports 11000-15000 to 51820.
- If you use OpenWrt, you can easily set up port forwarding rules in the firewall; port forwarding supports range mapping like 11000-15000.
- Now, external UDP access to ports 11000-15000 will be forwarded to 51820 internally, all serving the same WireGuard service.
On the External WireGuard Client, Use a Script to Automatically Switch Ports When the Link Fails
Take Windows as an example:
- Install the official WireGuard program for Windows.
- Prepare a directory (e.g., xxx) and put your WireGuard config file wg_home.conf in it, using the default port 51820.
- In the same directory, copy wg_home.conf and rename it to wg_home.conf.vm, replacing the default port 51820 with
${PORT}
. - Register wg_home.conf as a service:
wireguard /installtunnelservice C:\path\to\xxx\wg_home.conf
(run as administrator; if the wireguard command is not found, use the full path, e.g.,C:\Program Files\WireGuard\wireguard.exe /installtunnelservice C:\path\to\xxx\wg_home.conf
) - This will register a service that starts automatically: WireGuardTunnel$wg_home
- In the xxx directory, create check_wg.py and set it as a scheduled task in Windows to run every minute (search online for how to set this up), with administrator privileges.
- The content of check_wg.py is as follows (modify 10.250.250.2 to your peer's WireGuard IP; the default random port range is 11000-15000, adjust as needed to match your router's port range):
import os
import sys
import time
import datetime
import random
import subprocess
import shutil
# Log configuration
LOG_DIR = os.path.join(os.path.dirname(__file__), "logs")
LOG_FILE = os.path.join(LOG_DIR, datetime.datetime.now().strftime("%Y-%m-%d") + ".log")
MAX_DAYS = 3
# WireGuard service name (modify as needed)
WG_SERVICE_NAME = "WireGuardTunnel$wg_home" # Or your actual service name
def write_log(msg):
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now().isoformat()}] {msg}\n")
print(msg)
def clean_old_logs():
"""Keep only the last 3 days of log files"""
if not os.path.exists(LOG_DIR):
return
files = sorted(
[f for f in os.listdir(LOG_DIR) if f.endswith(".log")],
key=lambda x: x
)
if len(files) > MAX_DAYS:
for old_file in files[:-MAX_DAYS]:
try:
os.remove(os.path.join(LOG_DIR, old_file))
write_log(f"Deleted old log: {old_file}")
except Exception as e:
write_log(f"Failed to delete log: {old_file}, {e}")
def ping_target(ip):
"""Ping the target IP, return True if reachable, else False"""
cmd = ["ping", "-n", "1", "-w", "2000", ip]
try:
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="gbk")
return res.returncode == 0
except Exception as e:
write_log(f"Ping error: {e}")
return False
def pick_random_port():
return random.randint(11000, 15000)
def gen_wg_conf(port):
"""Generate wg_home.conf file"""
tpl_path = os.path.join(os.path.dirname(__file__), "wg_home.conf.vm")
out_path = os.path.join(os.path.dirname(__file__), "wg_home.conf")
if not os.path.exists(tpl_path):
write_log("Template file wg_home.conf.vm does not exist!")
return False
with open(tpl_path, "r", encoding="utf-8") as f:
content = f.read()
content = content.replace("${PORT}", str(port))
with open(out_path, "w", encoding="utf-8") as f:
f.write(content)
write_log(f"Generated wg_home.conf, port: {port}")
return True
def restart_wg_service():
"""Restart WireGuard service"""
try:
# Windows service name may differ, adjust as needed
stop_cmd = f'net stop "{WG_SERVICE_NAME}"'
start_cmd = f'net start "{WG_SERVICE_NAME}"'
print(stop_cmd)
print(start_cmd)
write_log("Stopping WireGuard service...")
subprocess.run(stop_cmd, shell=True)
time.sleep(2)
write_log("Starting WireGuard service...")
subprocess.run(start_cmd, shell=True, check=True)
write_log("WireGuard service restarted.")
except subprocess.CalledProcessError as e:
write_log(f"Failed to restart service: {e}")
def main():
clean_old_logs()
target_ip = "10.250.250.2" # Modify to your actual target
write_log(f"Checking connectivity to {target_ip} ...")
if ping_target(target_ip):
write_log(f"{target_ip} is reachable, exiting.")
sys.exit(0)
else:
write_log(f"{target_ip} is unreachable, preparing to switch WireGuard port...")
port = pick_random_port()
if not gen_wg_conf(port):
write_log("Failed to generate config, exiting.")
sys.exit(2)
restart_wg_service()
if __name__ == "__main__":
main()
- The principle of this Python script: it regularly checks the connectivity to the WireGuard peer. If reachable, it exits. If not, it randomly selects a port, renders wg_home.conf.vm with the new port, overwrites wg_home.conf, and restarts the WireGuard service. This way, you can bypass ISP blocking by using a new random port for WireGuard each time.