Learning Caddy - IP Filtering & Access Control
Episode 19 of 31

Learning Caddy - IP Filtering & Access Control

This episode covers IP-based access control: the remote_ip matcher with CIDR, whitelists and blacklists, the trusted_proxies directive for detecting the real IP, GeoIP filtering with plugins, and rate limiting for basic DDoS protection.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Not every visitor deserves entry. An admin panel should only be reachable from the office. Internal endpoints should only accept requests from specific services. Episode 19 covers IP-based access control in Caddy.

You'll learn the remote_ip matcher with CIDR notation for whitelists and blacklists, the trusted_proxies directive so Caddy knows the real client IP behind a proxy, GeoIP filtering with plugins for country-based control, and rate limiting to protect against attacks.

Layered access control makes a system far more resilient — and all of these tools are available in Caddy.

IP-Based Access Control

The remote_ip Matcher

The remote_ip matcher matches the client's IP address:

Restrict access to a subnet
example.com {
    @kantor {
        remote_ip 192.168.1.0/24
    }
    handle @kantor {
        root * /var/www/admin
        file_server
    }
    respond "Access denied" 403
}

Only clients from the 192.168.1.0/24 subnet can access /admin. Everyone else gets a 403. remote_ip accepts one or many addresses and CIDR ranges.

Whitelists and Blacklists

The whitelist pattern — only the listed IPs are allowed:

IP whitelist
example.com {
    @boleh {
        remote_ip 10.0.0.5 10.0.0.6 203.0.113.7
    }
    handle @boleh {
        reverse_proxy localhost:8080
    }
    respond "Not allowed" 403
}

The blacklist pattern — everyone except the blocked list:

IP blacklist
example.com {
    @dilarang {
        remote_ip 198.51.100.0/24
    }
    respond @dilarang "Blocked" 403
    root * /var/www
    file_server
}

A whitelist is stricter (deny by default); a blacklist is more open (allow by default). For sensitive resources, a whitelist is the safer choice.

Trusted Proxies

Why Caddy Needs to Know

When Caddy sits behind another proxy (a CDN, a cloud load balancer), the IP it sees isn't the real client IP. The X-Forwarded-For header contains the real IP, but that header can be spoofed. trusted_proxies tells Caddy which proxies are trustworthy:

Trusted proxies
{
    servers {
        trusted_proxies static 10.0.0.0/8 172.16.0.0/12
    }
}
 
example.com {
    @internal {
        remote_ip 192.168.1.0/24
    }
    handle @internal {
        respond "Internal"
    }
}

trusted_proxies static 10.0.0.0/8 marks every IP in that range as a trusted proxy. Only headers from trusted proxies are treated as the real IP.

Real Client IP Detection

With correct trusted_proxies, the remote_ip matcher works with the real client IP, not the proxy IP. Without it, every client appears to come from the CDN IP — making an office whitelist useless.

GeoIP Filtering

Using a GeoIP Plugin

Country-based control requires a plugin (episode 29). With the GeoIP module installed, block or allow based on country:

GeoIP blocking (with a plugin)
example.com {
    @terlarang {
        ip_country CN RU
    }
    respond @terlarang "Not available" 403
    root * /var/www
    file_server
}

ip_country CN RU (via the caddy-security or GeoIP module) blocks traffic from specific countries. Useful for:

  • Compliance: content restricted by region.
  • Security: blocking high-attack regions.
  • Licensing: services available only in certain countries.

Note: GeoIP based on IP addresses can be evaded with VPNs and isn't 100 percent accurate — treat it as one layer of defense, not the only one.

Rate Limiting

Basic DDoS Protection

Rate limiting limits the number of requests from a single IP within a time window. The caddy-rate-limit module (a plugin, episode 29) is used like this:

Rate limit per IP (with a plugin)
{
    order rate_limit before basicauth
}
 
example.com {
    rate_limit {
        zone api {
            key {remote_host}
            events 60
            window 1m
        }
    }
}

rate_limit limits 60 requests per minute per IP on the matching paths. Basic DDoS protection uses this pattern together with matchers:

Rate limit per endpoint
example.com {
    @login {
        path /api/login
    }
    rate_limit @login {
        zone login {
            key {remote_host}
            events 10
            window 1m
        }
    }
    reverse_proxy localhost:8080
}

The login endpoint is limited to 10 requests per minute per IP — hindering brute force. rate_limit @login applies the limit only to the login path.

Rate Limiting Considerations

  • Adjust thresholds to your users' normal traffic.
  • Don't be too strict — real users could get blocked too.
  • Combine with a whitelist for known bots.

Conclusion

Episode 19 built IP-based access control: the remote_ip matcher with CIDR for whitelists and blacklists, the trusted_proxies directive so real IPs are detected behind a proxy, GeoIP filtering for regional control, and rate limiting to protect sensitive endpoints.

Key takeaways:

  • remote_ip accepts IP addresses and CIDR ranges.
  • A whitelist denies by default; a blacklist allows by default.
  • trusted_proxies must be set when a proxy sits in front of Caddy.
  • GeoIP uses a plugin and can't be relied on alone.
  • Rate limiting protects login endpoints from brute force.
  • Layered access control is far harder to break.

In the next episode, episode 20, we'll cover security headers & CORS — the header directive for adding and removing headers, all the important security headers like CSP and HSTS, complete CORS configuration with OPTIONS preflight, and best practices for hiding server information.

Learning Caddy - IP Filtering & Access Control | Learning Caddy