HTTP Routing & Policies

Agentgateway provides powerful HTTP routing capabilities including path matching, header-based routing, rate limiting, retries, and request/response modification.

What you’ll build

In this tutorial, you configure the following.

  1. Configure HTTP routing with path, header, and query matching
  2. Create health check endpoints with direct responses
  3. Set up CORS, rate limiting, retries, and timeouts
  4. Implement IP-based authorization
  5. Modify requests and responses with header transformations

Before you begin

Step 1: Create a working directory

mkdir http-routing-test && cd http-routing-test

Step 2: Create a basic routing configuration

Create a config.yaml file with multiple routing examples.

cat > config.yaml << 'EOF'
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
binds:
- port: 3000
  listeners:
  - protocol: HTTP
    routes:
    # Health check - direct response without backend
    - name: health-check
      matches:
      - path:
          pathPrefix: /health
      policies:
        directResponse:
          body: '{"status": "healthy"}'
          status: 200

    # API route with path, query, and header matching
    - name: api-v2
      matches:
      - path:
          pathPrefix: /api
        method: GET
        query:
        - name: version
          value:
            exact: v2
        headers:
        - name: x-api-key
          value:
            regex: "key-[a-z0-9]+"
      policies:
        directResponse:
          body: '{"message": "API v2 matched!"}'
          status: 200

    # Route with CORS and rate limiting
    - name: protected-api
      matches:
      - path:
          pathPrefix: /protected
      policies:
        cors:
          allowHeaders: ["content-type", "authorization"]
          allowOrigins: ["https://example.com"]
          allowCredentials: true
          allowMethods: ["GET", "POST"]
          maxAge: 3600s
        localRateLimit:
        - maxTokens: 10
          tokensPerFill: 1
          fillInterval: 1s
        directResponse:
          body: '{"message": "Protected endpoint"}'
          status: 200

    # IP-based authorization
    - name: internal-only
      matches:
      - path:
          pathPrefix: /internal
      policies:
        authorization:
          rules:
          - |
            cidr("127.0.0.0/8").containsIP(source.address)
        directResponse:
          body: '{"message": "Internal access granted"}'
          status: 200

    # Catch-all route
    - name: default
      policies:
        directResponse:
          body: '{"error": "Not found"}'
          status: 404
EOF

Step 3: Start agentgateway

agentgateway -f config.yaml

Step 4: Test the routes

Test health check endpoint

curl http://localhost:3000/health

Expected response:

{"status": "healthy"}

Test path + query + header matching

The API v2 route requires all conditions to match.

# Missing query param and header - falls through to 404
curl http://localhost:3000/api
{"error": "Not found"}
# All conditions met - matches API v2
curl "http://localhost:3000/api?version=v2" -H "x-api-key: key-abc123"
{"message": "API v2 matched!"}

Test CORS preflight

curl -X OPTIONS http://localhost:3000/protected \
  -H "Origin: https://example.com" \
  -H "Access-Control-Request-Method: GET" \
  -i

Expected headers:

access-control-allow-origin: https://example.com
access-control-allow-methods: GET,POST
access-control-allow-headers: content-type,authorization
access-control-max-age: 3600

Test rate limiting

# Send 15 rapid requests
for i in $(seq 1 15); do
  code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/protected)
  echo "Request $i: $code"
done

After exceeding the rate limit (10 tokens), you’ll see 429 Too Many Requests.

Test IP-based authorization

# From localhost (IPv4) - should be allowed
curl http://127.0.0.1:3000/internal
{"message": "Internal access granted"}

Matching rules reference

Path matching

matches:
- path:
    pathPrefix: /api     # Matches /api, /api/users, /api/v1/...
- path:
    exact: /health       # Matches only /health
- path:
    regex: "/users/[0-9]+"  # Regex pattern

Header matching

matches:
- headers:
  - name: x-api-version
    value:
      exact: "2.0"
  - name: authorization
    value:
      regex: "Bearer .+"

Query parameter matching

matches:
- query:
  - name: format
    value:
      exact: json

Method matching

matches:
- method: GET    # Only match GET requests
- method: POST   # Only match POST requests

Traffic policies reference

Rate limiting

policies:
  localRateLimit:
  - maxTokens: 100      # Maximum burst size
    tokensPerFill: 10   # Tokens added per interval
    fillInterval: 1s    # Refill interval

Retries

policies:
  retry:
    attempts: 3
    codes: [502, 503, 504, 429]
    backoff:
      baseInterval: 100ms
      maxInterval: 1s

Timeouts

policies:
  timeout:
    requestTimeout: 30s
    idleTimeout: 60s

CORS

policies:
  cors:
    allowOrigins: ["https://example.com", "https://app.example.com"]
    allowMethods: ["GET", "POST", "PUT", "DELETE"]
    allowHeaders: ["content-type", "authorization"]
    exposeHeaders: ["x-request-id"]
    allowCredentials: true
    maxAge: 3600s

Request/Response header modification

policies:
  requestHeaderModifier:
    add:
      x-request-id: '${uuid()}'
    set:
      x-forwarded-for: '${source.address}'
    remove:
    - x-internal-header
  responseHeaderModifier:
    set:
      x-served-by: agentgateway

URL rewriting

policies:
  urlRewrite:
    path:
      full: "/new-path"
    authority:
      full: "backend.internal"

Request mirroring

policies:
  requestMirror:
    backend:
      host: 127.0.0.1:8081
    percentage: 0.1  # Mirror 10% of traffic

Direct response

policies:
  directResponse:
    body: '{"status": "ok"}'
    status: 200

IP-based authorization

policies:
  authorization:
    rules:
    # Allow specific CIDR ranges
    - |
      cidr("10.0.0.0/8").containsIP(source.address)
    # Or combine with OR logic
    - |
      cidr("192.168.0.0/16").containsIP(source.address) ||
      cidr("172.16.0.0/12").containsIP(source.address)

Routing with backends

When routing to actual backend services, use the following configuration.

routes:
- name: api-backend
  matches:
  - path:
      pathPrefix: /api
  policies:
    retry:
      attempts: 3
      codes: [502, 503, 504]
    timeout:
      requestTimeout: 30s
  backends:
  - host: api.internal:8080

- name: mcp-backend
  matches:
  - path:
      pathPrefix: /mcp
  policies:
    cors:
      allowOrigins: ["*"]
      allowHeaders: ["*"]
      exposeHeaders: ["Mcp-Session-Id"]
  backends:
  - mcp:
      targets:
      - name: everything
        stdio:
          cmd: npx
          args: ["@modelcontextprotocol/server-everything"]

Cleanup

Stop the agentgateway with Ctrl+C and remove the test directory.

cd .. && rm -rf http-routing-test

Learn more

Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

↑↓ navigate select esc dismiss

What could be improved?