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:
{
"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:
{
"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:
{
"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:
{
"paths": ["/api/users"],
"expectedRequestHeadersToUpstream": [
["$collectionHeaders"]
]
}3. Expansion at Runtime
HTTPTests expands $collectionHeaders to all defined headers:
// 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
{
"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
{
"collectionHeaders": [
["X-Client-Id"],
["X-Client-Version"],
["X-Platform"],
["X-Device-Type"]
]
}Useful for API gateways that need client information.
Custom Business Headers
{
"collectionHeaders": [
["X-Tenant-Id"],
["X-Organization-Id"],
["X-User-Id"]
]
}Multi-tenant applications can validate tenant context propagation.
Debug/Monitoring Headers
{
"collectionHeaders": [
["X-Debug-Mode"],
["X-Request-Start-Time"],
["X-Correlation-Id"]
]
}Validate monitoring and debugging headers reach services.
Examples
Basic Example
{
"collectionHeaders": [
["X-Request-Id"],
["X-Path"]
],
"hosts": {
"api.example.com": [
{
"paths": ["/health"],
"method": "GET",
"expectedStatus": 200,
"expectedRequestHeadersToUpstream": [
["$collectionHeaders"]
]
}
]
}
}Combining with Specific Headers
{
"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
{
"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
{
"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
// Good - Clear purpose
{
"collectionHeaders": [
["X-Request-Id"],
["X-Trace-Id"],
["X-Client-Version"]
]
}
// Bad - Unclear
{
"collectionHeaders": [
["X-Header-1"],
["X-Custom"],
["X-Data"]
]
}2. Group Related Headers
// 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
// 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
{
"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:
// 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:
- Check nginx configuration forwards headers:
location /api/ {
proxy_pass http://backend:80/;
proxy_pass_request_headers on; // Required!
}- Verify collection headers are defined:
{
"collectionHeaders": [ // Must be at top level
["X-Request-Id"]
],
"hosts": { ... }
}- Check header names match:
# Nginx
proxy_set_header X-Request-ID $request_id;
# Test
"collectionHeaders": [
["X-Request-ID"] // Must match exactly (case-insensitive)
]Related Topics
- test.json Reference - Complete test configuration
- Upstream Tracking - Validate proxy destinations
- Nginx Proxy Example - Practical usage