Skip to content
Back to skills

Cdn Rate Limiting

ASecurity

CDN caching and request rate limiting for Control Plane workloads. Use when the user asks about CDN, Cloudflare, CloudFront, edge caching, rate limiting, request throttling, per-key or per-route limits, or DDoS protection.

  • 10 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added August 31, 2026
devopsbashexpressawsgitapisecurity

Works with

  • cli
  • api
  • mcp

Security analysis

A92/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned October 2, 2026

npx -y skills add controlplane-com/ai-plugin --skill cdn-rate-limiting --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cdn Rate Limiting?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cdn Rate Limiting
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/controlplane-com-cdn-rate-limiting/badge)](https://www.skillsdirectory.com/skills/controlplane-com-cdn-rate-limiting)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cdn-rate-limiting
description: "CDN caching and request rate limiting for Control Plane workloads. Use when the user asks about CDN, Cloudflare, CloudFront, edge caching, rate limiting, request throttling, per-key or per-route limits, or DDoS protection."
---

# CDN & Rate Limiting

Two edge concerns, both built from existing primitives — there is no CDN or rate-limit resource kind. A **CDN** is bring-your-own (Cloudflare / CloudFront) pointed at the workload's canonical endpoint; **rate limiting** is an Envoy ratelimit service you deploy, enabled per workload by `cpln/rateLimit*` **tags**. Assumes the `workload` primer (firewall deny-by-default, canonical URL rules, create-then-verify).

## CDN

The pattern: the CDN proxies your domain and uses the workload's **canonical endpoint** as origin — read it from `status.canonicalEndpoint` or `mcp__cpln__list_deployments`, never construct it. A **workload endpoint** gives precise geo-routing and per-workload failover; a **GVC endpoint** serves one CDN route for many domains or wildcard subdomains, but keeps sending traffic to every location even when that location's workload is down.

### Cloudflare

1. **DNS at Cloudflare:** proxied CNAME (orange cloud on) from your subdomain to the canonical endpoint; SSL/TLS mode **Full (strict)**.
2. **Origin certificate** (SSL/TLS, then Origin Server; RSA 2048) becomes a Control Plane **TLS secret** — `create_secret` type `tls` with `values: "user"` (the user types the cert and key in the Console), **TLS chain left empty** (the origin cert is self-signed).
3. **Domain at Control Plane:** `mcp__cpln__create_domain` (CNAME DNS mode), `mcp__cpln__set_domain_tls` with the secret as the custom **server certificate**, `mcp__cpln__add_domain_route` to the workload. **The apex domain must be verified before configuring a subdomain.**

### Amazon CloudFront

1. **ACM public certificate in `us-east-1`** (CloudFront requires that region), DNS validation, covering your subdomain or wildcard.
2. **Distribution:** origin = the workload's canonical endpoint (BYOK: the per-location endpoint from `mcp__cpln__list_deployments`); alternate domain name = your subdomain; attach the ACM cert.
3. **DNS:** CNAME your subdomain to the distribution's `*.cloudfront.net` name.

### Lock out direct access

With a CDN in front, restrict the workload firewall so only CDN traffic reaches it: set `inboundAllowCIDR` to the provider's published ranges ([CloudFront IP ranges](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/LocationsOfEdgeServers.html)) via `mcp__cpln__update_workload`, and keep the list current. BYOK locations must also admit the ranges in the cluster's ingress security group — the CloudFront list is large, so raise the VPC quota for rules per security group to at least **530**. Details: **firewall-networking**.

## Rate limiting

Tags on the target workload inject an [Envoy rate-limit filter](https://github.com/envoyproxy/ratelimit) into its **inbound sidecar**: each request makes a gRPC check (1s timeout) against a ratelimit service you deploy (Envoy ratelimit + Redis); over-limit requests get **HTTP 429**.

### 1. Deploy the ratelimit stack

The stack is one multi-resource manifest from the [rate limiting guide](https://docs.controlplane.com/guides/rate-limiting): the **ratelimit** workload (`envoyproxy/ratelimit`), a **redis** workload, the **ratelimit-config** opaque secret (the rules), and the identity + policy for secret access. The user downloads and reviews it from the guide before it is applied. Create the GVC with `mcp__cpln__create_gvc`; no MCP tool applies a multi-resource manifest, so the apply uses the CLI:

```bash
cpln apply --file rate-limiting.yaml --org ORG --gvc ratelimit
```

The manifest assumes the GVC is named `ratelimit` (edit it if yours differs) and pins an older `envoyproxy/ratelimit` image tag; substitute a newer tag if desired.

**As shipped, the manifest is a trial setup, not a production one:** both workloads run `minScale: 1` with `spot: true`. Because enforcement is fail-closed, the ratelimit stack is tier-1 infrastructure for every tagged workload — for production raise its `minScale` to 2+, set `spot: false`, and run the GVC in the same locations as the workloads it protects (every request pays the check's round trip). A Redis restart only resets counters; a ratelimit outage denies traffic.

### 2. Configure the rules

Have the user edit the `ratelimit-config` opaque secret, [Envoy ratelimit format](https://github.com/envoyproxy/ratelimit#configuration); `unit`: `second` / `minute` / `hour` / `day`:

```yaml
domain: cpln
descriptors:
  - key: authorization
    rate_limit:
      unit: minute
      requests_per_unit: 10
```

After editing, reload the config: `cpln workload force-redeployment ratelimit --gvc ratelimit`.

### 3. Tag the target workload

Set with `mcp__cpln__update_workload` (or at creation):

| Tag | Required | Default | Meaning |
|---|:-:|---|---|
| `cpln/rateLimitAddress` | **Yes** | — | Canonical endpoint **hostname** of the ratelimit workload (no scheme prefix) — nothing happens without it |
| `cpln/rateLimitDescriptors` | **Effectively yes** | `authority` | Comma-separated: `authorization`, `host`, `path` — the default matches none of them, so **no limiting is applied until you set this** |
| `cpln/rateLimitScheme` | No | `https` | `https` dials the service over TLS with SNI |
| `cpln/rateLimitPort` | No | `443` | Port of the ratelimit service |
| `cpln/rateLimitDomain` | No | `cpln` | Must match `domain` in the config secret |

| Descriptor | Buckets per | HTTP header |
|---|---|---|
| `authorization` | API key / token | `Authorization` |
| `host` | domain | `Host` |
| `path` | endpoint | `:path` |

**There is no per-IP descriptor** — these three are the only ones wired. For per-client-IP throttling use the CDN layer (e.g. Cloudflare rate-limiting rules); keep this stack for per-token and per-route limits. Don't invent a `remote_address` descriptor — it does nothing.

### Combining descriptors

`cpln/rateLimitDescriptors: authorization,path` produces **one compound key** (an ordered tuple), not two independent limits. The config must **nest in the same order** as the tag's comma order:

```yaml
domain: cpln
descriptors:
  - key: authorization      # first tag entry
    descriptors:
      - key: path           # second tag entry — nested, not a sibling
        rate_limit:
          unit: minute
          requests_per_unit: 60
```

This buckets per token-and-path **pair** (any values). Add `value:` under a key to pin one route or token (e.g. `key: path, value: /api/search`). Two genuinely independent limits are not expressible through the tags — the filter emits a single action tuple.

**Anonymous-bypass warning:** Envoy skips the rate-limit check entirely when a descriptor header is missing from the request (`skip_if_absent` defaults to false and the filter doesn't set it). With `authorization` in the list, requests **without** an `Authorization` header are never checked — including the sample 10/minute config above. Limit by `path` or `host` (always present) when anonymous traffic matters, or throttle it at the CDN.

### Traps (from the filter's actual wiring)

- **Fail-closed:** the filter is configured with `failure_mode_deny: true` and a 1s check timeout — if the ratelimit service is down, unreachable, or the address is wrong, **inbound requests are denied**, not passed through. Verify the ratelimit workload is Ready (`mcp__cpln__list_deployments`) **before** tagging the target, and treat a typo in `cpln/rateLimitAddress` as an outage.
- **Descriptors must be explicit:** the built-in default `authority` produces an empty action list — the filter runs but limits nothing.
- **Tags are not validated:** these are plain tags interpreted by the platform; a misspelled tag name silently does nothing.
- The address is resolved by DNS from inside the mesh — use the ratelimit workload's exact canonical endpoint hostname.

## Layering the full stack

Traffic passes, in order: the **CDN** (absorbs and caches), then the **firewall** (`inboundAllowCIDR` = CDN IPs only, so nobody bypasses the CDN), then **rate limiting** (catches abuse that passes the CDN), then the workload.

## Verify the setup

- **Limiting works:** with the sample 10/minute rule, the 11th request within a minute that carries an `Authorization` header gets `429`. After any rules edit, `force-redeployment` the ratelimit workload first.
- **CDN serves:** `curl -I https://SUBDOMAIN` shows the CDN's header (`cf-cache-status` on Cloudflare, `x-cache` on CloudFront).
- **Bypass is closed:** `curl` the canonical endpoint directly — after the firewall lock-down it must no longer answer from outside the CDN ranges.

## Troubleshooting

| Symptom | Likely cause |
|---|---|
| Every request denied | Ratelimit service down/unready, or `cpln/rateLimitAddress` wrong — enforcement is fail-closed |
| Requests never limited | `cpln/rateLimitDescriptors` unset (default matches nothing); `domain` mismatch between tag and config; rules edited without `force-redeployment`; misspelled tag (tags are unvalidated) |
| Cloudflare 526 / TLS errors | Domain TLS not using the origin-cert secret, or SSL mode not Full (strict) |
| Redirect loop behind Cloudflare | SSL mode is Flexible — switch to Full (strict) |
| Origin still reachable directly | `inboundAllowCIDR` not restricted to the CDN ranges |

## CLI fallback

- CLI only: `cpln apply --file rate-limiting.yaml` (bundled manifest), `cpln workload force-redeployment` (config reload)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.