Local network tool that transparently redirects all X/Twitter traffic to xcancel.com, allowing you to browse Twitter content without directly accessing X's servers.
One-page cheat sheet for xcancel-forwarder
# Start services
docker compose up -d
# Start with specific compose file
docker compose -f docker-compose.caddy.yaml up -d
# View logs (follow mode)
docker compose logs -f nginx
# View logs (last 100 lines)
docker compose logs --tail=100 nginx
# Check container status
docker compose ps
# Restart services
docker compose restart
# Stop services
docker compose down
# Stop and remove volumes
docker compose down -v
# Update images and restart
docker compose pull && docker compose up -d
# Rebuild and restart
docker compose up -d --build
# Check DNS resolution
nslookup twitter.com
dig twitter.com
host twitter.com
# Test HTTP redirect
curl -I http://twitter.com
curl -I http://x.com
curl -I http://t.co/example
# Test HTTPS redirect
curl -I https://twitter.com
curl -I https://x.com
# Test with specific DNS server
nslookup twitter.com 192.168.1.100
dig @192.168.1.100 twitter.com
# Verbose curl (see full redirect chain)
curl -v http://twitter.com
# Follow redirects
curl -L http://twitter.com
# Check SSL certificate
openssl s_client -connect twitter.com:443 -servername twitter.com
# Check certificate expiry
openssl s_client -connect twitter.com:443 -servername twitter.com 2>/dev/null | openssl x509 -noout -dates
# Install mkcert
brew install mkcert # macOS
apt install mkcert # Debian/Ubuntu
dnf install mkcert # Fedora
# Create CA and install
mkcert -install
# Generate certificates
mkcert twitter.com x.com "*.twitter.com" "*.x.com" t.co "*.t.co"
# Check CA location
mkcert -CAROOT
# Uninstall CA
mkcert -uninstall
# Create CA private key
openssl genrsa -out ca.key 4096
# Create CA certificate
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
# Create server private key
openssl genrsa -out server.key 2048
# Create certificate signing request
openssl req -new -key server.key -out server.csr
# Sign certificate with CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 825
# Verify certificate
openssl verify -CAfile ca.crt server.crt
# View certificate details
openssl x509 -in server.crt -text -noout
# Check certificate expiry
openssl x509 -in server.crt -noout -dates
# macOS
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
# Linux (systemd-resolved)
sudo systemd-resolve --flush-caches
# Linux (nscd)
sudo /etc/init.d/nscd restart
# Windows (PowerShell)
ipconfig /flushdns
# Chrome/Edge - navigate to:
chrome://net-internals/#dns
# Click "Clear host cache"
# Firefox - navigate to:
about:networking#dns
# Click "Clear DNS Cache"
# Safari
# Develop menu → Empty Caches
# Pi-hole (via SSH)
pihole restartdns
# dnsmasq
docker compose restart dnsmasq
# Or with systemd
sudo systemctl restart dnsmasq
# Find Docker host IP
ip addr show
ifconfig
# Test connectivity to nginx
curl http://NGINX_IP
curl https://NGINX_IP
# Check port availability
netstat -tuln | grep -E ':(80|443)'
ss -tuln | grep -E ':(80|443)'
# Test from specific interface
curl --interface eth0 http://twitter.com
# Trace HTTP request
curl -v -L http://twitter.com 2>&1 | grep -E '(> |< )'
# Check Docker network
docker network ls
docker network inspect xcancel-forwarder_default
docker network inspect xcancel-forwarder_macvlan_lan
| File | Purpose | Default Location |
|---|---|---|
.env |
Environment variables | Project root |
docker-compose.yaml |
nginx orchestration | Project root |
docker-compose.caddy.yaml |
Caddy orchestration | Project root |
nginx/nginx.conf |
nginx main config | nginx/nginx.conf |
nginx/conf.d/xcancel-redirect.conf |
Redirect rules | nginx/conf.d/ |
nginx/ssl/server.crt |
SSL certificate | nginx/ssl/ |
nginx/ssl/server.key |
SSL private key | nginx/ssl/ |
caddy/Caddyfile |
Caddy config | caddy/Caddyfile |
caddy/ssl/server.crt |
Caddy SSL cert | caddy/ssl/ |
caddy/ssl/server.key |
Caddy SSL key | caddy/ssl/ |
dnsmasq/dnsmasq.conf |
dnsmasq config | dnsmasq/dnsmasq.conf |
# nginx access log (inside container)
docker compose exec nginx tail -f /var/log/nginx/access.log
# nginx error log (inside container)
docker compose exec nginx tail -f /var/log/nginx/error.log
# dnsmasq query log (if enabled)
docker compose logs -f dnsmasq
# Docker compose logs
docker compose logs -f
| Port | Service | Protocol | Notes |
|---|---|---|---|
| 80 | nginx/Caddy | HTTP | Redirect port |
| 443 | nginx/Caddy | HTTPS | SSL redirect port |
| 53 | dnsmasq | DNS | UDP/TCP (if using dnsmasq) |
| Description | Example | Configure In |
|---|---|---|
| Docker host IP | 192.168.1.10 | .env → NGINX_HOST_IP |
| nginx macvlan IP | 192.168.1.100 | .env → NGINX_IP |
| dnsmasq macvlan IP | 192.168.1.101 | .env → DNSMASQ_IP |
| LAN subnet | 192.168.1.0/24 | .env → LAN_SUBNET |
| LAN gateway | 192.168.1.1 | .env → LAN_GATEWAY |
| DNS upstream | 1.1.1.1 | dnsmasq.conf → server= |
| Variable | Purpose | Example | Required |
|---|---|---|---|
NGINX_IP |
nginx macvlan IP | 192.168.1.100 |
If using macvlan |
DNSMASQ_IP |
dnsmasq macvlan IP | 192.168.1.101 |
If using dnsmasq |
LAN_SUBNET |
Network CIDR | 192.168.1.0/24 |
If using macvlan |
LAN_GATEWAY |
Default gateway | 192.168.1.1 |
If using macvlan |
LAN_INTERFACE |
Host interface | eth0 |
If using macvlan |
http://pi.hole/admintwitter.com → nginx IPx.com → nginx IPt.co → nginx IP*.twitter.com → twitter.com*.x.com → x.com# A records
address=/twitter.com/192.168.1.100
address=/x.com/192.168.1.100
address=/t.co/192.168.1.100
# Wildcard subdomains (included in address= above)
twitter.com → 192.168.1.100
*.twitter.com → 192.168.1.100
x.com → 192.168.1.100
*.x.com → 192.168.1.100
t.co → 192.168.1.100
/etc/hosts192.168.1.100 twitter.com www.twitter.com
192.168.1.100 x.com www.x.com
192.168.1.100 t.co
C:\Windows\System32\drivers\etc\hosts192.168.1.100 twitter.com www.twitter.com
192.168.1.100 x.com www.x.com
192.168.1.100 t.co
# Check what DNS server you're using
cat /etc/resolv.conf # Linux/macOS
ipconfig /all # Windows
# Test specific DNS server
dig @192.168.1.100 twitter.com # Query dnsmasq directly
nslookup twitter.com 1.1.1.1 # Query Cloudflare (should NOT return nginx IP)
# Check nginx is running
docker compose ps | grep nginx
# Check nginx config syntax
docker compose exec nginx nginx -t
# View nginx access log
docker compose logs nginx | grep twitter
# Test directly with IP
curl -I http://NGINX_IP
# Check certificate validity
openssl s_client -connect twitter.com:443 -servername twitter.com 2>/dev/null | openssl x509 -noout -dates
# Check certificate SANs
openssl s_client -connect twitter.com:443 -servername twitter.com 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
# Check CA is trusted (macOS)
security find-certificate -c "mkcert" -a
# Check CA is trusted (Linux)
ls /usr/local/share/ca-certificates/
# Check CA is trusted (Firefox - separate CA store)
# about:preferences#privacy → View Certificates → Authorities
# Check Docker host cannot reach macvlan (this is normal)
ping 192.168.1.100 # From Docker host - will fail
# Check from another device
ping 192.168.1.100 # From laptop/phone - should work
# Check IP isn't already in use
nmap -sn 192.168.1.0/24
# Verify macvlan network exists
docker network ls | grep macvlan
# Check detailed error
docker compose logs nginx
# Check ports aren't in use
sudo netstat -tuln | grep -E ':(80|443)'
# Check SELinux/AppArmor (Linux)
sudo setenforce 0 # Temporarily disable SELinux
sudo aa-status # Check AppArmor
# Check file permissions
ls -la nginx/ssl/
git clone https://github.com/ryantenney/xcancel-forwarder.git
cd xcancel-forwarder
python3 scripts/setup-wizard.py
# Follow prompts, done!
# Clone
git clone https://github.com/ryantenney/xcancel-forwarder.git
cd xcancel-forwarder
# Config
cp .env.example .env
vim .env
# SSL
brew install mkcert
mkcert -install
mkcert twitter.com x.com "*.twitter.com" "*.x.com" t.co "*.t.co"
mv *.pem nginx/ssl/
mv nginx/ssl/*-key.pem nginx/ssl/server.key
mv nginx/ssl/*.pem nginx/ssl/server.crt
# Start
docker compose up -d
# Configure DNS (choose method from guides)
# Clone
git clone https://github.com/ryantenney/xcancel-forwarder.git
cd xcancel-forwarder
# Use Caddy
docker compose -f docker-compose.caddy.yaml up -d
# Configure DNS to point to Docker host IP
# No SSL setup needed
nslookup twitter.comcurl -I http://twitter.comcurl -I https://twitter.comhttp://twitter.comhttps://twitter.comhttps://mobile.twitter.comhttps://www.x.comhttp://t.co/exampleDocumentation:
Troubleshooting:
docker compose logs -fCommon Fix: 90% of issues are DNS cache. Clear all caches (client, browser, DNS server).