# Custom Domains & WAF

## Custom domains

A custom domain needs a hosted zone, a certificate, and DNS records pointing to the load balancer. Set `domain.dns` according to where the hosted zone is managed.

| Mode | The zone | What Keel does | Manual steps |
|---|---|---|---|
| `route53` *(default)* | Already exists in this AWS account | Writes validation + alias records; the apply waits for the certificate | None |
| `route53-managed` | Keel creates it | Everything, once you set the nameservers at your registrar | Set 4 nameservers |
| `external` | Stays where it is (Cloudflare, another account, corporate DNS) | Issues the certificate; prints the records; writes none | Create the records, then `keel domains verify` |

```bash
keel domains add app.example.com          # looks the zone up once and records the mode
keel domains add app.example.com --dns route53-managed
keel domains add app.example.com --dns external
keel domains status                       # live state: zone, delegation, certificate, what's left (--json)
keel domains records                      # the records to create by hand, in provider-form shape
keel domains verify                       # recheck; for external, records an issued certificate (--wait, --timeout 15m)
keel domains remove app.example.com
```

`domains add` supports `--zone`, `--zone-id`, `--dns`, repeatable `--alias`, `--certificate-arn`, and `--no-force-https`. It updates the target environment in `keel.yml`; run `keel up` to apply the change in AWS.

Declared in `keel.yml`:

```yaml
domain:
  name: app.example.com
  zone: example.com          # required for the route53 modes; ignored for external
  # dns: route53             # route53 | route53-managed | external
  # aliases:
  #   - www.app.example.com
  #   - "*.app.example.com"  # on the certificate, deliberately not routed
  # certificate_arn: arn:aws:acm:...   # bring your own; Keel then issues nothing
  # certificate_issued: true # external only — written by 'keel domains verify'
  # force_https: true        # takes effect only once a certificate exists
  # validation_timeout: 20m
```

### HTTPS activation

While a certificate is pending, port 80 continues to serve the application. `force_https` defaults to `true` and starts redirecting only after the certificate is issued and attached.

### Delegation checks

`keel domains status` queries public DNS instead of relying only on Route 53, so it can detect a registrar that still delegates to another provider. It reports the next required step in order: zone, delegation, certificate, then attachment. For apex domains it also notes that DNS providers must use ALIAS or ANAME records rather than CNAME.

### Retained managed zones

Keel retains hosted zones created with `route53-managed` when you run `domains remove` or `keel destroy`. Recreating a zone assigns different nameservers and would invalidate registrar delegation and remove records Keel may not manage. Delete a retained zone manually in AWS when it is no longer needed; AWS charges $0.50 per month for it.

### External DNS takes two applies

With `dns: external`, run `keel up` to provision HTTP and print the validation records. Add those records at your DNS provider, run `keel domains verify --wait`, then run `keel up` again to attach HTTPS. Static sites use the same sequence with `keel sites verify`.

## WAF

Keel supports AWS WAF with managed rule sets to protect your ALB. WAF requires `load_balancer.enabled: true` — and therefore at least one service; a sites-only app has no ALB to attach it to.

```yaml
waf:
  enabled: true
  managed_rules:
    - AWSManagedRulesCommonRuleSet
```

```bash
keel waf enable                # default rule set: AWSManagedRulesCommonRuleSet
keel waf enable --rules AWSManagedRulesCommonRuleSet,AWSManagedRulesSQLiRuleSet
keel waf status
keel waf disable
```

Changes are applied on the next `keel up`.
