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.