# Best Practices

Practical guidance for building, testing, and debugging EaaS tenant configurations.

## Security Configuration

### Start with Core Protections

Enable essential WAF groups first, then add more as you monitor false positives:

```json
{
  "security_config": {
    "deny_groups": [
      "SQL-INJECTION-ANOMALY",
      "XSS-ANOMALY",
      "CMD-INJECTION-ANOMALY"
    ]
  }
}
```

### Use Specific Exceptions

When adding exceptions, scope them as narrowly as possible. Combine multiple exception types (paths + IPs + ASNs) to avoid over-permitting:

```json
{
  "security_config": {
    "exceptions": {
      "SQL-INJECTION-ANOMALY": {
        "paths": ["/api/search"],
        "v4_ips": ["192.0.2.0/24"]
      }
    }
  }
}
```

Avoid broad exceptions like exempting an entire `/api/` prefix or a `/0` CIDR block from all rules.

### Rate Control Tiers

Security groups `IPBLOCK-BURST` and `IPBLOCK-SUMMARY` provide rate limiting. The thresholds are configured at the security policy level. Common tiers:

| Posture | Burst / Summary | Protection Level |
|---|---|---|
| Relaxed | 200 / 180 | Low — lenient, few false positives |
| Balanced | 35 / 30 | Moderate — good default |
| Protective | 25 / 20 | Substantial — some false positives expected |
| Strict | 10 / 8 | High — aggressive, higher false positive risk |

## Delivery Configuration

### Rule Ordering

Most delivery features use a rule-based structure where **the first matching rule wins**. Order rules from most specific to least specific:

```json
{
  "caching": {
    "rules": [
      {
        "matchAll": { "paths": ["/api/v2/users/*"] },
        "args": { "bypass": true }
      },
      {
        "matchAll": { "paths": ["/api/*"] },
        "args": { "ttl_seconds": 300 }
      },
      {
        "args": { "ttl_seconds": 3600 }
      }
    ]
  }
}
```

In this example: user API requests bypass cache, other API requests cache for 5 minutes, everything else caches for 1 hour. If the broad `/api/*` rule were first, the specific `/api/v2/users/*` rule would never fire.

### Match Condition Efficiency

- **Prefer prefix matching** (`paths_startswith`) over wildcard matching (`paths_wildcard`) when possible — it's faster.
- **Use `matchAll`** (AND) for common cases. Use `matchAny` (OR) only when you genuinely need "any of these conditions."
- **Keep match arrays reasonable.** Each match condition supports up to 100 values, but smaller arrays evaluate faster.

### Simple vs Rule-Based

Some features accept both a simple value and a rule-based structure. Use the simple form when you don't need conditional logic:

```json
// Simple: applies to all requests
{ "connectTimeout": 5000 }

// Rule-based: different timeout for different paths
{
  "connectTimeout": {
    "rules": [
      {
        "matchAll": { "paths": ["/slow-api/*"] },
        "args": { "value": 30000 }
      }
    ]
  }
}
```

## Testing Your Configuration

### 1. Validate Against the Schema

Before deploying, validate your tenant JSON:

- **Online:** Paste your config into the [Tenant Validator](/validator)
- **CLI:** Use `check-jsonschema` with the schema file:

```bash
pip install check-jsonschema
check-jsonschema --schemafile tenant-schema.json my-tenant.json
```

- **IDE:** Point your editor's JSON Schema validation at the [schema file](/tenant-schema.json) for real-time feedback.

### 2. Use Debug Headers

EaaS supports debug response headers that reveal how the edge processed your request. Add the debug header to your request:

```bash
curl -s -D - -o /dev/null \
  -H "x-eaas-debug: <your-debug-key>" \
  https://your-tenant-hostname.example.com/test-path
```

The debug key is configured via the `PMUSER_EAAS_DEBUG_KEY` variable in your Property Manager configuration. Contact your Akamai representative if you don't know your debug key.

### 3. Bypass the Tenant Cache

The EdgeWorker caches tenant files briefly (approximately 3 minutes). When testing configuration changes, bypass the cache to force a fresh read:

```bash
curl -s -D - -o /dev/null \
  -H "x-eaas-debug: <your-debug-key>" \
  -H "x-bypass-lru: true" \
  https://your-tenant-hostname.example.com/test-path
```

### 4. Test on Staging First

Always test configuration changes on the Akamai staging network before activating on production. Use `--resolve` to point your request at the staging IP:

```bash
curl --resolve "your-hostname.example.com:443:<staging-ip>" \
  -H "x-eaas-debug: <your-debug-key>" \
  https://your-hostname.example.com/test-path
```

## Debug Response Headers

When the `x-eaas-debug` header is included in your request, the edge returns these response headers:

### General

| Header | Description |
|---|---|
| `x-eaas-tenant-log` | Summary log line: GRN, deny reason, sub-request GRN, LRU cache object size, sub-request status code |
| `x-eaas-route-used` | Which conditional origin was used by the route feature |

### Caching

| Header | Description |
|---|---|
| `x-eaas-edgeworker-ttl` | TTL value set by the EdgeWorker |
| `x-eaas-cache-match` | Whether a caching rule was triggered. Values: `no-store`, `bypass`, or TTL in seconds |
| `x-eaas-honor-origin` | Whether origin cache-control headers are being honored |
| `x-eaas-must-revalidate` | Whether must-revalidate is enabled |
| `x-eaas-internal-ttl` | Actual TTL on the edge. `-1` means not cacheable |
| `x-eaas-content-source` | Where the response was served from (cache, origin, etc.) |
| `x-eaas-current-age` | Current age of the cached object |

### Tenant Log Format

The `x-eaas-tenant-log` header value follows this format:

```
grn=<request-grn>,deny=<deny-reason>,srq_code=<status>,srq_grn=<subrequest-grn>,lru_obj_size=<bytes>
```

| Field | Description |
|---|---|
| `grn` | Global Request Number — unique identifier for this request |
| `deny` | WAF deny reason (empty if not denied) |
| `srq_code` | HTTP status code from the tenant file sub-request (200 = OK) |
| `srq_grn` | GRN of the sub-request used to fetch the tenant file |
| `lru_obj_size` | Size of the tenant file in the EdgeWorker's LRU cache (bytes) |

## Schema Limits Reference

Key limits defined in the tenant schema:

| Property | Limit |
|---|---|
| Tenant file size | Recommended under 10KB |
| `tenant_id` (string) | Max 20 characters |
| `tenant_id` (array) | Max 5 entries |
| `ttl_seconds` | 0 — 31,536,000 seconds (0 to 1 year) |
| `connectTimeout` | 50 — 120,000 ms |
| `firstByteTimeout` | 50 — 120,000 ms |
| `readTimeout` | 50 — 120,000 ms |
| Match condition arrays | Max 100 values per condition |
| Security exception arrays | Max 50 values per exception field |
