> ## Documentation Index
> Fetch the complete documentation index at: https://docs.buttons.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# SSRF protection

> How Buttons blocks HTTP requests to private network addresses.

Server-Side Request Forgery (SSRF) is a class of attack where an attacker tricks a service into making HTTP requests to internal addresses — cloud metadata endpoints, internal APIs, or localhost services — that should not be reachable from outside.

HTTP buttons execute outbound HTTP requests. By default, Buttons blocks any request that resolves to a private network address.

## Blocked address ranges

The following ranges are blocked by default:

| Range | Description |
| - | - |
| `127.0.0.0/8` | IPv4 loopback |
| `10.0.0.0/8` | RFC 1918 private |
| `172.16.0.0/12` | RFC 1918 private |
| `192.168.0.0/16` | RFC 1918 private |
| `169.254.0.0/16` | RFC 3927 link-local (includes AWS/GCP/Azure metadata: `169.254.169.254`) |
| `100.64.0.0/10` | RFC 6598 carrier-grade NAT |
| `0.0.0.0/8` | "This network" (RFC 791) |
| `224.0.0.0/4` | IPv4 multicast (RFC 5771) |
| `240.0.0.0/4` | IPv4 reserved (RFC 1112) |
| `::1/128` | IPv6 loopback |
| `fc00::/7` | IPv6 unique local |
| `fe80::/10` | IPv6 link-local |
| `ff00::/8` | IPv6 multicast |

## Two enforcement points

A blocked target is caught at one of two places, depending on whether the URL's host is a literal IP or a hostname.

**Literal IP, before any request.** If the post-substitution URL's host is a literal IP inside a blocked range, the press is refused before a connection is attempted. This surfaces as a `VALIDATION_ERROR`:

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "refusing request: 169.254.169.254 resolves to a blocked private range"
  }
}
```

**Hostname, at dial time.** A hostname is resolved and every A/AAAA record is checked against the blocklist before the connection is dialed (see DNS rebinding below). A blocked address surfaces as a `SCRIPT_ERROR`:

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "SCRIPT_ERROR",
    "message": "request failed: SSRF blocked: metadata.internal.example resolves to private address 169.254.169.254 (set --allow-private-networks on the button or BUTTONS_ALLOW_PRIVATE_NETWORKS=1 to override)"
  }
}
```

## DNS rebinding protection

Validation happens after DNS resolution, not just on the literal URL. This prevents DNS rebinding attacks, where a public hostname resolves to a private IP.

The safe dialer resolves the hostname once, rejects the request if **any** resolved A/AAAA record lands in a blocked range, and then dials by the resolved IP rather than re-resolving the hostname. Dialing by the already-checked IP closes the rebinding window where an attacker returns a public IP on the first lookup and a private IP on the second.

So if the hostname `api.attacker.example` resolves to `10.0.0.1` at press time, the request is blocked even though the URL does not look like a private address.

## Escape hatch 1: per-button flag

To allow a specific button to target a private address, pass `--allow-private-networks` at create time. The flag applies only to HTTP (`--url`) buttons:

```bash theme={null}
buttons create local-health \
  --url 'http://127.0.0.1:8080/health' \
  --allow-private-networks \
  -d "Check local service health"
```

This flag is stored in the button spec and applies every time that button is pressed.

<Warning>
  Only use `--allow-private-networks` for buttons that target infrastructure you control. A button with this flag in a shared environment gives any agent that can press it a path to your internal network.
</Warning>

## Escape hatch 2: environment variable

To allow all HTTP buttons to target private networks in a given environment, set:

```bash theme={null}
BUTTONS_ALLOW_PRIVATE_NETWORKS=1 buttons press local-service --arg path=/metrics
```

This is useful in local development or in isolated container networks where all traffic is internal by design.

## Example: internal service in a container network

```bash theme={null}
# In a Docker network where services communicate over private IPs
BUTTONS_ALLOW_PRIVATE_NETWORKS=1 buttons create fetch-metrics \
  --url 'http://metrics-service:9090/metrics' \
  -d "Scrape Prometheus metrics from the internal service"
```

Or set the env var once in your container:

```dockerfile theme={null}
ENV BUTTONS_ALLOW_PRIVATE_NETWORKS=1
```

## Related

* [HTTP API buttons](/buttons/http-api) — `--allow-private-networks` flag reference
* [Security overview](/security/overview) — full threat model
* [Template encoding](/security/template-encoding) — injection protection for URL and body values


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.