
Intro #
Detect VPNs and residential proxies with Caddy to identify and control high-risk traffic such as automated scrapers, credential stuffing bots, or users bypassing geofencing. Anonymizing networks, specifically VPNs and residential proxies, are frequently used to mask bad actors behind legitimate-looking or rotating IP addresses.
In this tutorial, you will learn how to configure the Caddy web server on Debian 13 to detect and block traffic from VPNs and residential proxies using the IP2Proxy PX10 MMDB database.
Overview & Architecture #
Why Use Caddy for VPN and Proxy Detection? #
Caddy is an open-source, enterprise-ready web server written in Go. Unlike traditional web servers, Caddy features automatic HTTPS via Let’s Encrypt/ZeroSSL, simplified Caddyfile syntax, and a modular architecture that allows custom plugins to run directly within the HTTP pipeline.
Why Use IP2Proxy PX10 MMDB for VPN and Proxy Detection? #
The IP2Proxy PX10 database provides comprehensive proxy detection, identifying proxy types such as VPNs, TOR exit nodes, public proxies, web proxies, and residential proxies. When supplied in the MMDB binary format, it exposes standardized boolean flags:
is_anonymousis_anonymous_vpnis_residential_proxyis_tor_exit_nodeis_public_proxyis_hosting_provider
Because the IP2Proxy MMDB file adheres strictly to this schema, Caddy’s GeoIP2 module can inspect incoming client IP addresses in real time with near-zero latency.
Step 1: Install Dependencies on Debian 13 #
Make sure all system packages are up to date.
sudo apt update && sudo apt upgrade -y
Install essential tools, Git, and Go (required to build Caddy with custom plugins):
sudo apt install -y curl wget git build-essential golang-go
Verify the Go installation:
go version

Step 2: Install xcaddy and Build Caddy with the GeoIP2 Module #
Standard binary builds of Caddy do not ship with MMDB inspection enabled out of the box. We will use xcaddy (Caddy’s official builder tool) to compile a Caddy binary containing the caddy-geoip2 module.
Install xcaddy: #
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
sudo mv ~/go/bin/xcaddy /usr/local/bin/
Build Caddy with the caddy-geoip2 module: #
xcaddy build --with github.com/zhangjiayin/caddy-geoip2
Install the custom Caddy binary system-wide: #
sudo mv caddy /usr/bin/caddy
caddy version
Verify the GeoIP2 module is included: #
caddy list-modules | grep geoip2

Set up Caddy system users and directories: #
sudo groupadd --system caddy
sudo useradd --system \
--gid caddy \
--create-home \
--home-dir /var/lib/caddy \
--shell /usr/sbin/nologin \
--comment "Caddy web server" \
caddy
sudo mkdir -p /etc/caddy
sudo mkdir -p /etc/caddy/mmdb
Step 3: Prepare the IP2Proxy PX10 MMDB Database #
Subscribe to the IP2Proxy PX10 if you don’t have a subscription. Then, login to the user area and download the zipped file containing the PX10 MMDB. Extract the IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL.MMDB and upload to your home directory on the server.
Next, move your MMDB file to /etc/caddy/mmdb/ (below is our home directory, modify yours accordingly if you have a different home directory):
sudo mv /home/admin/IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL.MMDB /etc/caddy/mmdb/GeoIP2-Anonymous-IP.mmdb
Set proper ownership so Caddy can access the file:
sudo chown -R caddy:caddy /etc/caddy/mmdb
sudo chmod 644 /etc/caddy/mmdb/GeoIP2-Anonymous-IP.mmdb
Step 4: Configure Caddy (Caddyfile) #
Create or edit your Caddyfile at /etc/caddy/Caddyfile: #
sudo nano /etc/caddy/Caddyfile
Paste the following configuration and save the file (remember to change to your own domain):
{
# Tell Caddy to evaluate geoip2_vars early in the request pipeline
order geoip2_vars first
geoip2 {
databaseDirectory "/etc/caddy/mmdb"
editionID "GeoIP2-Anonymous-IP"
}
}
# Replace example.com with your actual domain or server IP
example.com {
# Enables GeoIP variable extraction for incoming requests
# 'strict' enforces client IP matching directly from the socket connection
geoip2_vars strict
# Named Matcher: Flag requests coming from VPNs
@is_vpn {
vars {geoip2.is_anonymous_vpn} true
}
# Named Matcher: Flag requests coming from Residential Proxies
@is_res_proxy {
vars {geoip2.is_residential_proxy} true
}
# Action 1: Handle VPN Traffic
handle @is_vpn {
respond "Access Denied: Virtual Private Networks (VPN) are not permitted." 403 {
close
}
}
# Action 2: Handle Residential Proxy Traffic
handle @is_res_proxy {
respond "Access Denied: Residential proxy traffic detected." 403 {
close
}
}
# Default Action: Serve content for connections other than VPN or residential proxy
handle {
respond "Access Granted" 200
}
}
Validate your Caddyfile formatting: #
caddy validate --config /etc/caddy/Caddyfile

Step 5: Configure Systemd and Start Caddy #
Create a systemd unit file to manage the Caddy service: #
sudo nano /etc/systemd/system/caddy.service
Paste the following service definition:
[Unit]
Description=Caddy Web Server
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
Reload systemd, enable, and start Caddy: #
sudo systemctl daemon-reload
sudo systemctl enable --now caddy
sudo systemctl status caddy

Step 6: Testing & Verification #
Test 1: Direct/Clean Request #
Make a request from a standard broadband/mobile connection:

Test 2: Request via VPN #
Connect through a commercial VPN provider, then visit the page:

Conclusion #
By coupling Caddy’s module architecture with the IP2Proxy PX10 MMDB dataset, you gain an effective perimeter defense layer. Anonymized VPN traffic and residential proxies are evaluated and blocked directly at the HTTP layer, preventing unnecessary application workload, database operations, or API consumption downstream.
