# Domain Rules

Filter device traffic using destination domain names and wildcard patterns.

**Traffic Filtering** domain rules match outbound traffic by destination FQDN (domain name). For an overview of Traffic Filtering and how rules are evaluated, see [Traffic Filtering](https://docs.soracom.io/en/services/vpg/traffic-filtering). To configure rules in the User Console or via the Soracom API, see [Configuration](https://docs.soracom.io/en/services/vpg/traffic-filtering/configuration).

## Wildcard Patterns

FQDN patterns may use a single leading wildcard label (`*.example.com`). A wildcard pattern matches **only single-level subdomains**: `*.example.com` matches `api.example.com` and `www.example.com`, but does **not** match `sub.api.example.com` (a multi-level subdomain).

| Pattern | Valid? | Reason |
| - | - | - |
| `*.example.com` | ✓ | The wildcard is the leading label in its entirety. |
| `foo*.example.com`, `*foo.example.com` | | The wildcard must be the entire leading label, not concatenated with other characters within it. |
| `example.*.com` | | The wildcard can only be specified in the leading label, not in the middle of the pattern. |
| `*.*.example.com` | | Only a single wildcard is allowed per pattern. |

## Domain Rule Priority

Domain rules use the same most-specific-match model as IP rules, with **domain name match** replacing CIDR range as the primary match type. Priority is determined independently for each type:

| Type | Priority |
| - | - |
| Domain name | A more specific pattern (e.g., `api.example.com`) takes precedence over a broader pattern (e.g., `*.example.com`). |
| Protocol | Specified protocol takes precedence over unspecified (ANY / `255`). |
| Port | Specified port takes precedence over unspecified. |
| Port range | Narrower range takes precedence over broader range. |

> [!NOTE]
>
> Domain rules currently support only `action: "allow"`. The most-specific-match priority is consequential mainly when domain rules are combined with a default-deny IP rule. See [Example 4: FQDN Allow with Default-Deny](https://docs.soracom.io/en/services/vpg/traffic-filtering/configuration#example-4-fqdn-allow-with-default-deny).

## DNS Resolution and Dynamic Rules

FQDN matching is driven by **passive monitoring of plaintext DNS** on the standard DNS ports (**UDP/53** and **TCP/53**). From the DNS queries and responses the device exchanges with its DNS server, the platform reads the queried domain name and the resolved IP addresses, and transparently generates **dynamic IP rules** from those addresses (one `/32` entry per resolved IP, copying the protocol, port, and action from the domain rule). Each match is recorded against the queried name for that session.

### Supported DNS Servers

| DNS server | Supported |
| - | - |
| SORACOM-provided DNS (`100.127.0.53`, `100.127.1.53`) | ✓ |
| Custom DNS server configured via Group Configuration | ✓ |
| Any DNS server configured on the device | ✓ |

> [!NOTE]
>
> Domain filtering requires **plaintext DNS on UDP/53 or TCP/53**. The platform reads the queried domain name and resolved IP addresses from DNS traffic on those ports only. Devices or applications that use **encrypted DNS** (DoH, DoT) or non-standard DNS ports bypass this monitoring and will not trigger domain rules; configure them to use plaintext DNS on the standard ports.

### Dynamic IP Rules

Dynamic IP rules generated from FQDN matches are conceptually equivalent to the static IP rules you configure, but with two important properties:

- They are created and removed automatically based on monitored DNS resolutions; you cannot list or edit them directly.
- **They do not count toward the [500-rule limit](https://docs.soracom.io/en/services/vpg/traffic-filtering#limitations)** for IP rules.

For example, when a domain rule for `*.example.com` matches a DNS resolution of `www.example.com` that returns two IP addresses (`203.0.113.40` and `203.0.113.48`), two dynamic IP rules are generated: one `/32` rule for each IP, carrying the protocol/port/action from the domain rule.

Each dynamic IP rule inherits the following fields from its parent domain rule:

| Field | Value in dynamic rule |
| - | - |
| `cidr` | Resolved IP address with a `/32` prefix |
| `protocol` | Inherited from the domain rule |
| `fromPort`, `toPort` | Inherited from the domain rule |
| `action` | Always `"allow"` |

## What Domain Filtering Cannot Control

Domain rules match traffic based on the **domain name resolved by DNS**, not on application-layer attributes of the request. As a result, certain HTTP-level distinctions cannot be expressed as domain rules:

| Capability | Supported | Reason |
| - | - | - |
| Allow/deny by domain name (e.g., `api.example.com`) | ✓ | Determined at the time of DNS resolution. |
| Allow/deny by URL path (e.g., `/foo` vs `/bar`) | | URL paths are not visible at the DNS layer. |
| Allow/deny by query parameter (e.g., `?id=123`) | | Query parameters are not visible at the DNS layer. |

For example, you cannot allow `https://api.example.com/foo` while denying `https://api.example.com/bar`. Both URLs resolve to the same domain (and the same IP set), so they are treated identically by Traffic Filtering.
