Skip to content

Collection Headers

Learn how to use collection headers to reduce duplication in your tests and maintain consistent header validation across multiple endpoints.

The Problem

When testing proxy configurations, you often need to validate the same headers across many endpoints:

json
{
    "hosts": {
        "api.example.com": [
            {
                "paths": ["/users"],
                "expectedRequestHeadersToUpstream": [
                    ["X-Request-Id"],
                    ["X-Trace-Id"],
                    ["X-Client-Version"],
                    ["X-Forwarded-For"]
                ]
            },
            {
                "paths": ["/orders"],
                "expectedRequestHeadersToUpstream": [
                    ["X-Request-Id"],
                    ["X-Trace-Id"],
                    ["X-Client-Version"],
                    ["X-Forwarded-For"]
                ]
            },
            {
                "paths": ["/payments"],
                "expectedRequestHeadersToUpstream": [
                    ["X-Request-Id"],
                    ["X-Trace-Id"],
                    ["X-Client-Version"],
                    ["X-Forwarded-For"]
                ]
            }
        ]
    }
}

Problems:

  • ❌ Repetitive and verbose
  • ❌ Hard to maintain
  • ❌ Easy to make mistakes
  • ❌ Difficult to add/remove common headers

The Solution

Define headers once in collectionHeaders and reuse them with $collectionHeaders:

json
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Trace-Id"],
        ["X-Client-Version"]
    ],
    "hosts": {
        "api.example.com": [
            {
                "paths": ["/users"],
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],
                    ["X-Forwarded-For"]
                ]
            },
            {
                "paths": ["/orders"],
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],
                    ["X-Forwarded-For"]
                ]
            },
            {
                "paths": ["/payments"],
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],
                    ["X-Forwarded-For"]
                ]
            }
        ]
    }
}

Benefits:

  • ✅ DRY (Don't Repeat Yourself)
  • ✅ Easy to maintain
  • ✅ Consistent across all tests
  • ✅ Change once, apply everywhere

How It Works

1. Define Collection Headers

At the top level of your test.json, define collectionHeaders:

json
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Trace-Id"]
    ],
    "hosts": { ... }
}

Format: Array of header arrays, same as expectedRequestHeadersToUpstream

  • ["Header-Name"] - Header must exist (any value)
  • ["Header-Name", "value"] - Header must exist with specific value

2. Reference in Tests

Use $collectionHeaders in your test cases:

json
{
    "paths": ["/api/users"],
    "expectedRequestHeadersToUpstream": [
        ["$collectionHeaders"]
    ]
}

3. Expansion at Runtime

HTTPTests expands $collectionHeaders to all defined headers:

json
// This...
{
    "expectedRequestHeadersToUpstream": [
        ["$collectionHeaders"],
        ["X-Forwarded-For"]
    ]
}

// ...becomes this at runtime:
{
    "expectedRequestHeadersToUpstream": [
        ["X-Request-Id"],
        ["X-Trace-Id"],
        ["X-Forwarded-For"]
    ]
}

Common Use Cases

Distributed Tracing Headers

json
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Trace-Id"],
        ["X-Span-Id"],
        ["X-Parent-Span-Id"]
    ]
}

All endpoints in a distributed system should forward these headers.

Client Identification Headers

json
{
    "collectionHeaders": [
        ["X-Client-Id"],
        ["X-Client-Version"],
        ["X-Platform"],
        ["X-Device-Type"]
    ]
}

Useful for API gateways that need client information.

Custom Business Headers

json
{
    "collectionHeaders": [
        ["X-Tenant-Id"],
        ["X-Organization-Id"],
        ["X-User-Id"]
    ]
}

Multi-tenant applications can validate tenant context propagation.

Debug/Monitoring Headers

json
{
    "collectionHeaders": [
        ["X-Debug-Mode"],
        ["X-Request-Start-Time"],
        ["X-Correlation-Id"]
    ]
}

Validate monitoring and debugging headers reach services.

Examples

Basic Example

json
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Path"]
    ],
    "hosts": {
        "api.example.com": [
            {
                "paths": ["/health"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"]
                ]
            }
        ]
    }
}

Combining with Specific Headers

json
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Trace-Id"]
    ],
    "hosts": {
        "api.example.com": [
            {
                "paths": ["/proxy/users"],
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],
                    ["X-Forwarded-For"],
                    ["X-Real-IP"],
                    ["Host", "api.example.com"],
                    ["X-Upstream-Target", "backend:5001"]
                ]
            }
        ]
    }
}

Result: Test validates 6 headers total (2 from collection + 4 specific).

Headers with Expected Values

json
{
    "collectionHeaders": [
        ["X-API-Version", "v1"],
        ["X-Environment", "production"]
    ],
    "hosts": {
        "api.example.com": [
            {
                "paths": ["/api/users"],
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"]
                ]
            }
        ]
    }
}

Validates headers exist with specific values.

Real-World Microservices Example

json
{
    "collectionHeaders": [
        ["X-Request-ID"],
        ["X-Client-Version"]
    ],
    "hosts": {
        "gateway.microservices.local": [
            {
                "paths": ["/users/profile"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],
                    ["X-Forwarded-For"],
                    ["X-Real-IP"],
                    ["X-Service-Name", "user-service"],
                    ["X-Gateway", "api-gateway"]
                ]
            },
            {
                "paths": ["/orders/12345"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],
                    ["X-Forwarded-For"],
                    ["X-Real-IP"],
                    ["X-Service-Name", "order-service"],
                    ["X-Gateway", "api-gateway"]
                ]
            },
            {
                "paths": ["/payments/status"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],
                    ["X-Forwarded-For"],
                    ["X-Real-IP"],
                    ["X-Service-Name", "payment-service"],
                    ["X-Gateway", "api-gateway"]
                ]
            }
        ]
    }
}

When to Use Collection Headers

✅ Use Collection Headers When:

  • Multiple endpoints need the same headers

    json
    {
        "collectionHeaders": [["X-Request-Id"]],
        "hosts": {
            "api.example.com": [
                {"paths": ["/users"], "expectedRequestHeadersToUpstream": [["$collectionHeaders"]]},
                {"paths": ["/orders"], "expectedRequestHeadersToUpstream": [["$collectionHeaders"]]},
                {"paths": ["/payments"], "expectedRequestHeadersToUpstream": [["$collectionHeaders"]]}
            ]
        }
    }
  • Testing distributed tracing across services

    json
    {
        "collectionHeaders": [
            ["X-Trace-Id"],
            ["X-Span-Id"],
            ["X-Parent-Span-Id"]
        ]
    }
  • Validating consistent client headers

    json
    {
        "collectionHeaders": [
            ["X-Client-Id"],
            ["X-Client-Version"],
            ["X-Platform"]
        ]
    }

❌ Don't Use Collection Headers When:

  • Headers are endpoint-specific

    json
    // Bad - These are specific to each endpoint
    {
        "collectionHeaders": [
            ["X-Upstream-Target", "backend:5001"]  // Different per endpoint
        ]
    }
    
    // Good - Define per test
    {
        "paths": ["/users"],
        "expectedRequestHeadersToUpstream": [
            ["X-Upstream-Target", "user-service:5001"]
        ]
    },
    {
        "paths": ["/orders"],
        "expectedRequestHeadersToUpstream": [
            ["X-Upstream-Target", "order-service:5002"]
        ]
    }
  • Only testing one or two endpoints

    json
    // Bad - No benefit from collection
    {
        "collectionHeaders": [["X-Request-Id"]],
        "hosts": {
            "api.example.com": [
                {
                    "paths": ["/health"],
                    "expectedRequestHeadersToUpstream": [["$collectionHeaders"]]
                }
            ]
        }
    }
    
    // Good - Direct definition
    {
        "hosts": {
            "api.example.com": [
                {
                    "paths": ["/health"],
                    "expectedRequestHeadersToUpstream": [["X-Request-Id"]]
                }
            ]
        }
    }

Best Practices

1. Name Headers Descriptively

json
// Good - Clear purpose
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Trace-Id"],
        ["X-Client-Version"]
    ]
}

// Bad - Unclear
{
    "collectionHeaders": [
        ["X-Header-1"],
        ["X-Custom"],
        ["X-Data"]
    ]
}
json
// Good - Tracing headers together
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Trace-Id"],
        ["X-Span-Id"]
    ]
}

// Bad - Mixed purposes
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-API-Version"],
        ["X-Trace-Id"],
        ["Authorization"]
    ]
}

3. Don't Overuse

json
// Good - Common headers only
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Client-Version"]
    ]
}

// Bad - Too many, some are specific
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Client-Version"],
        ["X-Upstream-Target"],  // Endpoint-specific!
        ["X-Service-Name"],      // Endpoint-specific!
        ["Content-Type"],        // Not always the same
        ["Authorization"]        // Not always present
    ]
}

4. Combine with Specific Headers

json
{
    "collectionHeaders": [
        ["X-Request-Id"],
        ["X-Trace-Id"]
    ],
    "hosts": {
        "api.example.com": [
            {
                "paths": ["/users"],
                "expectedRequestHeadersToUpstream": [
                    ["$collectionHeaders"],           // Common
                    ["X-Service-Name", "user-service"], // Specific
                    ["X-Upstream-Target", "user-service:5001"] // Specific
                ]
            }
        ]
    }
}

Troubleshooting

Collection Headers Not Expanding

Issue: $collectionHeaders appears literally in error messages

Cause: Typo in the placeholder

Solutions:

json
// Correct
["$collectionHeaders"]

// Wrong
["$collection_headers"]  // Underscore instead of dash
["$CollectionHeaders"]   // Wrong capitalization
["collectionHeaders"]    // Missing $

Headers Not Found

Issue: Test fails with "Expected header not found"

Solutions:

  1. Check nginx configuration forwards headers:
nginx
location /api/ {
    proxy_pass http://backend:80/;
    proxy_pass_request_headers on;  // Required!
}
  1. Verify collection headers are defined:
json
{
    "collectionHeaders": [  // Must be at top level
        ["X-Request-Id"]
    ],
    "hosts": { ... }
}
  1. Check header names match:
nginx
# Nginx
proxy_set_header X-Request-ID $request_id;

# Test
"collectionHeaders": [
    ["X-Request-ID"]  // Must match exactly (case-insensitive)
]

Released under the MIT License.