How Sell-Side Platforms Can Build Prebid Video Caching Infrastructure Before Microsoft Shutters Xandr

A technical guide for SSPs transitioning away from Xandr's video caching services to self-hosted Prebid infrastructure before Microsoft's shutdown deadline.

How Sell-Side Platforms Can Build Prebid Video Caching Infrastructure Before Microsoft Shutters Xandr

How Sell-Side Platforms Can Build Prebid Video Caching Infrastructure Before Microsoft Shutters Xandr

The ad tech industry has seen its share of seismic shifts, but few have created as much operational urgency as Microsoft's decision to wind down Xandr's core advertising technology services. For sell-side platforms that have relied on Xandr's video caching infrastructure, this announcement represents both a significant challenge and an unexpected opportunity to modernize their video advertising stack. The clock is ticking. SSPs that have built dependencies on Xandr's caching services now face a choice: scramble to find another third-party provider, or take control of their destiny by building proprietary Prebid-based video caching infrastructure. This article argues strongly for the latter approach and provides a comprehensive roadmap for making it happen.

Understanding the Stakes: Why Video Caching Matters

Before diving into implementation details, it's worth stepping back to understand why video caching sits at such a critical juncture in the programmatic video supply chain. Video advertising operates under fundamentally different constraints than display. When a video player requests an ad, it expects a VAST (Video Ad Serving Template) response that it can parse and render within milliseconds. The player doesn't have the luxury of waiting for a complex auction to complete, creative assets to be fetched from origin servers, and wrapper chains to be resolved. This is where caching becomes essential. Video caching serves several critical functions in the programmatic ecosystem:

  • Latency reduction: Pre-caching VAST responses and creative assets ensures that video ads can be served within the tight timing windows that video players demand
  • Wrapper resolution: Video ad serving often involves multiple VAST wrappers that need to be unwrapped before reaching the actual media file. Caching allows this unwrapping to happen asynchronously
  • Bid preservation: In header bidding scenarios, bids need to be stored and retrieved reliably between the auction and the actual ad serve
  • Error handling: Caching provides a buffer that allows for graceful degradation when upstream servers experience issues

For SSPs, the ability to reliably cache and serve video ads directly impacts fill rates, publisher satisfaction, and ultimately revenue. A poorly performing video caching layer can result in blank video slots, frustrated users, and publishers looking for alternative monetization partners.

The Xandr Dependency Problem

Many SSPs, particularly those that emerged during the 2015-2020 period of rapid programmatic video growth, built their video stacks with Xandr's infrastructure as a foundational component. This wasn't unreasonable at the time. Xandr (then AppNexus) offered robust, battle-tested video caching that integrated cleanly with their broader advertising technology suite. The dependency typically manifests in several ways:

  • Direct caching API integration: SSPs making direct calls to Xandr's caching endpoints to store and retrieve VAST responses
  • Prebid Server hosted instances: Running Prebid Server on Xandr's infrastructure, which includes their caching layer
  • Creative transcoding pipelines: Relying on Xandr's media processing for format conversion and quality optimization
  • Analytics and reporting: Using Xandr's infrastructure for video event tracking and reconciliation

With Microsoft's announcement, each of these integration points becomes a liability that needs to be addressed. The timeline for transition varies depending on specific contractual arrangements, but prudent SSPs are treating this as a 6-12 month window to achieve complete independence.

Why Build Rather Than Buy?

The natural question that arises is whether SSPs should simply migrate to another third-party caching provider rather than building their own infrastructure. While there are scenarios where this makes sense, I would argue that for most mid-to-large SSPs, building proprietary infrastructure is the superior long-term strategy. Here's the reasoning:

  • Control over the critical path: Video caching sits directly in the monetization critical path. Any outage or performance degradation at your caching provider translates directly into lost revenue. Owning this infrastructure means owning your uptime
  • Cost optimization at scale: Third-party caching is typically priced on a per-request or per-gigabyte basis. At scale, these costs can become substantial. Self-hosted infrastructure, while requiring upfront investment, typically offers better unit economics for high-volume SSPs
  • Customization and innovation: Generic caching solutions are designed for the average use case. Building your own allows you to optimize for your specific traffic patterns, publisher mix, and competitive differentiation
  • Avoiding future dependency: The Xandr situation illustrates the risk of building core infrastructure dependencies on third parties. Building in-house insulates you from future platform risk
  • Data sovereignty: Keeping bid data, creative information, and serving logs within your own infrastructure simplifies compliance with privacy regulations and gives you fuller insight into your supply chain

The counter-argument typically centers on engineering resources and time-to-market. These are legitimate concerns, but the Prebid ecosystem has matured to the point where building video caching infrastructure is no longer a green-field engineering exercise. The building blocks exist; what's required is thoughtful assembly and customization.

Prebid Video Caching Architecture: A Technical Deep Dive

Prebid's approach to video caching is designed around the concept of a cache that sits between the auction process and the video player's ad request. Understanding this architecture is essential before beginning implementation.

The Request Flow

In a typical Prebid video implementation, the flow works as follows:

  1. The publisher's page loads and initializes Prebid.js with video ad units defined
  2. Prebid conducts a header bidding auction, collecting bids from various SSP and exchange partners
  3. For video bids, the VAST XML or VAST URL is received from demand partners
  4. Prebid stores the VAST content in a cache, receiving a cache ID in return
  5. The winning bid information, including the cache ID, is passed to the ad server
  6. When the video player requests the ad, it retrieves the VAST from the cache using the cache ID
  7. The player parses the VAST and renders the video creative This flow introduces several architectural components that need to work together seamlessly:
    [Publisher Page] --> [Prebid.js] --> [SSP Bidders]
    |
    v
    [Cache Store] <-- [Cache ID]
    |
    v
    [Video Player] --> [VAST Retrieval]

    Prebid Server and the Cache Module

    Prebid Server, the server-side complement to Prebid.js, includes a cache module that handles video caching natively. This is where most SSPs should focus their implementation efforts. The cache module in Prebid Server supports multiple backend storage options:

    • In-memory caching: Suitable for development and testing, but not recommended for production due to lack of persistence and horizontal scaling limitations
    • Redis/Valkey: A popular choice that offers good performance and supports clustering for horizontal scale
    • Aerospike: Higher performance option used by several major ad tech platforms, though with additional operational complexity
    • Custom backends: Prebid Server's architecture allows for custom cache implementations to integrate with existing infrastructure

    For most SSPs building new infrastructure, Redis (or its open-source fork Valkey) represents the sweet spot of performance, operational familiarity, and ecosystem support.

    Configuration Deep Dive

    Let's look at a practical Prebid Server configuration for video caching. The following YAML configuration demonstrates a production-ready setup:

    cache:
    scheme: https
    host: cache.your-ssp.com
    path: /cache
    query: uuid=%PBS_CACHE_UUID%
    # Backend configuration
    backend:
    type: redis
    host: redis-cluster.internal.your-ssp.com
    port: 6379
    password: ${REDIS_PASSWORD}
    db: 0
    # Connection pool settings
    pool:
    max_idle: 50
    max_active: 200
    idle_timeout: 240s
    # Cluster mode for horizontal scaling
    cluster:
    enabled: true
    nodes:
    - redis-node-1.internal:6379
    - redis-node-2.internal:6379
    - redis-node-3.internal:6379
    # TTL settings
    default_ttl_seconds: 300
    # Size limits
    max_value_size_bytes: 10485760  # 10MB max for video creatives

    This configuration establishes several important parameters: The external-facing cache URL structure uses a UUID-based retrieval pattern, which is the standard expected by Prebid.js and video players. The Redis backend is configured in cluster mode for horizontal scaling, with connection pooling to handle burst traffic efficiently. The TTL (time-to-live) setting of 300 seconds (5 minutes) is a reasonable starting point for video caching. This provides enough time for the auction-to-serve latency while not consuming excessive storage for bids that don't win.

    The Cache Endpoint Implementation

    When building your cache service, you'll need to implement two primary endpoints: POST /cache - Store content and return a cache ID

    func (s *CacheService) Store(w http.ResponseWriter, r *http.Request) {
    var request CacheRequest
    if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
    http.Error(w, "Invalid request body", http.StatusBadRequest)
    return
    }
    responses := make([]CacheResponse, len(request.Puts))
    for i, put := range request.Puts {
    // Generate unique cache ID
    cacheID := generateCacheUUID()
    // Determine content type and prepare storage
    var content []byte
    if put.Type == "xml" {
    content = []byte(put.Value)
    } else if put.Type == "json" {
    content, _ = json.Marshal(put.Value)
    }
    // Store with TTL
    ttl := s.defaultTTL
    if put.TTLSeconds > 0 {
    ttl = time.Duration(put.TTLSeconds) * time.Second
    }
    if err := s.redis.Set(r.Context(), cacheID, content, ttl).Err(); err != nil {
    s.logger.Error("Cache store failed", "error", err, "cacheID", cacheID)
    responses[i] = CacheResponse{UUID: ""}
    continue
    }
    responses[i] = CacheResponse{UUID: cacheID}
    }
    json.NewEncoder(w).Encode(CachePutResponse{Responses: responses})
    }

    GET /cache - Retrieve content by cache ID

    func (s *CacheService) Retrieve(w http.ResponseWriter, r *http.Request) {
    cacheID := r.URL.Query().Get("uuid")
    if cacheID == "" {
    http.Error(w, "Missing uuid parameter", http.StatusBadRequest)
    return
    }
    content, err := s.redis.Get(r.Context(), cacheID).Bytes()
    if err == redis.Nil {
    http.Error(w, "Cache entry not found", http.StatusNotFound)
    return
    } else if err != nil {
    s.logger.Error("Cache retrieval failed", "error", err, "cacheID", cacheID)
    http.Error(w, "Internal server error", http.StatusInternalServerError)
    return
    }
    // Detect content type and set appropriate headers
    if bytes.HasPrefix(content, []byte("<?xml")) || bytes.HasPrefix(content, []byte("<VAST")) {
    w.Header().Set("Content-Type", "application/xml")
    } else {
    w.Header().Set("Content-Type", "application/json")
    }
    // CORS headers for cross-origin video player requests
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
    w.Write(content)
    }

    VAST Wrapper Handling

    One of the more complex aspects of video caching is handling VAST wrappers. A VAST response might contain multiple levels of wrappers, each pointing to another URL that needs to be resolved before the actual media file can be located. For optimal performance, your caching layer should implement wrapper unwrapping:

    func (s *CacheService) UnwrapVAST(ctx context.Context, vastXML []byte, maxDepth int) ([]byte, error) {
    if maxDepth <= 0 {
    return nil, errors.New("max wrapper depth exceeded")
    }
    var vast VASTResponse
    if err := xml.Unmarshal(vastXML, &vast); err != nil {
    return nil, fmt.Errorf("failed to parse VAST: %w", err)
    }
    // Check if this is a wrapper
    if vast.Ad.Wrapper != nil && vast.Ad.Wrapper.VASTAdTagURI != "" {
    // Fetch the wrapped VAST
    wrappedVAST, err := s.fetchVAST(ctx, vast.Ad.Wrapper.VASTAdTagURI)
    if err != nil {
    return nil, fmt.Errorf("failed to fetch wrapped VAST: %w", err)
    }
    // Recursively unwrap
    return s.UnwrapVAST(ctx, wrappedVAST, maxDepth-1)
    }
    // This is an inline VAST, return as-is
    return vastXML, nil
    }

    Be cautious with wrapper unwrapping, as it introduces additional latency and potential failure points. Many SSPs opt for a hybrid approach where wrappers are unwrapped asynchronously after the initial cache store, with the unwrapped version replacing the original once processing completes.

    Infrastructure Considerations

    Building the cache service is only part of the equation. The surrounding infrastructure needs to be designed for the specific demands of video advertising traffic.

    CDN Strategy

    Your cache retrieval endpoint will receive requests from video players running on end-user devices around the world. This traffic pattern demands a CDN layer in front of your cache service. Key CDN considerations for video caching:

    • Geographic distribution: Video players are latency-sensitive. Your CDN should have edge locations that minimize round-trip time to major markets
    • Cache-Control headers: Configure your cache service to emit appropriate Cache-Control headers that allow CDN caching while respecting TTLs
    • Origin shielding: Use origin shield layers to reduce the load on your cache backend during traffic spikes
    • HTTPS everywhere: Video players increasingly require HTTPS. Ensure your CDN handles TLS termination efficiently

    A typical CDN configuration might look like this in your cache service:

    func (s *CacheService) setCDNHeaders(w http.ResponseWriter, ttl time.Duration) {
    // Allow CDN caching for the remaining TTL
    maxAge := int(ttl.Seconds())
    if maxAge > 0 {
    w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", maxAge))
    } else {
    w.Header().Set("Cache-Control", "no-cache, no-store")
    }
    // Vary header for proper cache keying
    w.Header().Set("Vary", "Accept-Encoding")
    // Edge cache tags for selective purging
    w.Header().Set("Cache-Tag", "video-cache")
    }

    Redis Cluster Sizing and Topology

    The Redis (or Valkey) cluster backing your cache needs to be sized appropriately for your traffic patterns. Here are the key factors to consider: Memory requirements: Calculate based on your expected cache entry count and average size. For video VAST responses, assume 5-50KB per entry depending on whether you're storing full VAST or just URLs.

    Memory needed = (peak concurrent auctions) × (entries per auction) × (average entry size) × (safety factor)
    Example: 100,000 QPS × 3 entries × 20KB × 2.0 = 12GB

    Read/Write ratio: Video cache workloads are typically read-heavy, with a 10:1 or higher read-to-write ratio. This favors Redis cluster configurations with read replicas. Cluster topology: For a production deployment, consider:

    • Minimum 3 master nodes: Required for cluster consensus
    • 1-2 replicas per master: Provides read scaling and failover capability
    • Cross-availability-zone deployment: Ensures resilience to infrastructure failures

    Monitoring and Alerting

    Video cache failures manifest immediately as blank ad slots and angry publishers. Robust monitoring is non-negotiable. Essential metrics to track:

    • Cache hit rate: Should be near 100% for retrieval requests (misses indicate TTL issues or data loss)
    • Latency percentiles: P50, P95, and P99 latency for both store and retrieve operations
    • Error rates: Connection failures, timeout rates, and Redis errors
    • Memory utilization: Redis memory usage relative to available capacity
    • Eviction rate: If Redis is evicting entries before TTL expiration, you need more memory

    Sample Prometheus metrics exposition:

    var (
    cacheOperations = prometheus.NewCounterVec(
    prometheus.CounterOpts{
    Name: "video_cache_operations_total",
    Help: "Total video cache operations",
    },
    []string{"operation", "status"},
    )
    cacheLatency = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
    Name:    "video_cache_operation_duration_seconds",
    Help:    "Video cache operation latency",
    Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1},
    },
    []string{"operation"},
    )
    )

    Migration Strategy: From Xandr to Self-Hosted

    With the technical architecture understood, let's discuss the practical migration path from Xandr-hosted caching to your self-hosted infrastructure.

    Phase 1: Parallel Running (Weeks 1-4)

    Begin by deploying your new cache infrastructure alongside the existing Xandr integration. Configure Prebid Server to write to both caches simultaneously:

    cache:
    # Primary: new self-hosted cache
    primary:
    scheme: https
    host: cache.your-ssp.com
    path: /cache
    # Secondary: Xandr cache (for fallback during migration)
    secondary:
    scheme: https
    host: prebid-server.xandr.com
    path: /cache
    # Write to both, read from primary with fallback
    strategy: dual-write-primary-read

    This approach allows you to validate that your new infrastructure is receiving and storing data correctly without any risk to production traffic.

    Phase 2: Traffic Shifting (Weeks 5-8)

    Gradually shift read traffic from Xandr to your self-hosted cache. A percentage-based rollout provides control:

    • Week 5: 10% of traffic to new cache
    • Week 6: 25% of traffic to new cache
    • Week 7: 50% of traffic to new cache
    • Week 8: 90% of traffic to new cache, with Xandr as fallback only

    Monitor error rates and latency closely during each increment. Have clear rollback criteria defined before beginning.

    Phase 3: Cutover (Weeks 9-10)

    Complete the migration by removing Xandr dependencies entirely:

    • Disable dual-write: Stop writing to Xandr cache
    • Remove fallback configuration: Point all traffic to self-hosted infrastructure
    • Update DNS and CDN configurations: Ensure all cache URLs resolve to your infrastructure
    • Decommission Xandr integration code: Remove technical debt once stability is confirmed

    Phase 4: Optimization (Weeks 11-12)

    With migration complete, focus on optimization:

    • TTL tuning: Analyze actual cache hit patterns and adjust TTLs for optimal balance of freshness and storage efficiency
    • Cost optimization: Right-size Redis clusters based on observed usage patterns
    • Performance tuning: Implement additional optimizations like connection pooling adjustments, compression, or tiered caching

    Advanced Considerations

    Multi-Format Support

    Modern video advertising spans multiple formats beyond traditional desktop video. Your caching infrastructure should account for:

    • CTV/OTT: Connected TV environments often have different latency requirements and may benefit from longer cache TTLs
    • Mobile in-app video: SDK-based video players may have different VAST parsing capabilities that affect what you cache
    • Outstream video: Outstream formats have different timing characteristics, potentially allowing for more aggressive caching

    Consider implementing format-aware caching logic:

    func (s *CacheService) getTTLForFormat(format string) time.Duration {
    switch format {
    case "ctv":
    return 10 * time.Minute  // Longer TTL for CTV
    case "mobile":
    return 3 * time.Minute   // Slightly shorter for mobile
    case "outstream":
    return 5 * time.Minute   // Standard for outstream
    default:
    return 5 * time.Minute   // Default
    }
    }

    Creative Caching vs. VAST Caching

    The discussion so far has focused on VAST response caching, but there's a related consideration around creative asset caching. The video files themselves can be substantial (megabytes rather than kilobytes), and their caching has different characteristics. Options to consider:

    • Pass-through: Don't cache creative assets; let them be served from DSP/creative CDNs. Simplest approach but introduces latency
    • CDN-level caching: Configure your CDN to cache creative assets from upstream origins. Good balance of simplicity and performance
    • Full creative caching: Download and store creative assets in your own object storage (S3, GCS). Maximum control but significant storage and bandwidth costs

    For most SSPs, CDN-level caching of creative assets represents the pragmatic middle ground.

    Privacy and Compliance Considerations

    Video caching infrastructure handles bid-level data that may include user identifiers or signals derived from user data. Ensure your implementation addresses:

    • Data minimization: Don't cache more data than necessary for the caching function
    • Appropriate TTLs: Short TTLs naturally limit data retention
    • Audit logging: Maintain logs of cache access for compliance purposes, but be thoughtful about what you log
    • Geographic considerations: If you serve EU traffic, ensure your cache infrastructure can support data residency requirements

    The Business Case

    For readers who need to justify this investment internally, here's how to frame the business case: Risk mitigation: The cost of video monetization disruption during an unplanned Xandr migration would far exceed the investment in proactive infrastructure development. A single day of video fill rate degradation can represent significant revenue impact for a mid-sized SSP. Long-term cost reduction: Third-party caching typically costs $0.50-2.00 per million requests at scale. Self-hosted infrastructure, once amortized, often achieves costs of $0.10-0.30 per million requests. Competitive differentiation: Owning your video infrastructure enables optimizations and innovations that aren't possible with generic third-party solutions. Publisher confidence: Publishers evaluating SSP partners increasingly ask about infrastructure resilience and dependency management. Owning critical infrastructure demonstrates operational maturity.

    Conclusion

    The sunsetting of Xandr's advertising technology services represents a significant disruption for SSPs that have built dependencies on their infrastructure. However, it also presents an opportunity to invest in self-hosted video caching infrastructure that provides better control, improved economics, and reduced platform risk. The technical path forward is clearer than it might initially appear. Prebid Server provides a solid foundation for video caching, Redis/Valkey offers a proven storage backend, and the architectural patterns are well-established in the industry. What's required is focused execution and a realistic migration timeline. For SSPs that haven't yet begun planning their Xandr transition, the time to start is now. A 12-week migration timeline is achievable but doesn't leave much room for unexpected complications. Begin with infrastructure planning and proof-of-concept development while your existing Xandr integration continues to function. The sell-side platforms that navigate this transition successfully will emerge with more robust, cost-effective video infrastructure. Those that delay risk scrambling through a hasty migration or, worse, experiencing monetization disruption that damages publisher relationships. The choice is clear, and the tools to succeed are available. The only question is whether your organization will commit to the investment in time to execute it properly. Building resilient, self-owned infrastructure isn't just about surviving the Xandr shutdown. It's about positioning your SSP for long-term success in an industry where technical capability increasingly determines competitive outcomes. Video advertising will only grow in importance as CTV expands and attention shifts to video content across all screens. The infrastructure investments you make today will pay dividends for years to come.