RTB Architecture

    Prebid Server Cookie Sync and User Sync Explained: /cookie_sync, /setuid and the uids Cookie

    Published · Last updated
    Next scheduled review:

    Prebid Server cookie sync is how a server-side auction learns each bidder's ID for a browser. Prebid.js calls /cookie_sync, Prebid Server returns sync pixels for bidders it cannot yet identify, and each bidder redirects to /setuid. The server stores the IDs in a uids cookie on its own domain and sends them in later auctions.

    Key terms on this page: Prebid Server · cookie sync · user matching · Prebid.js · TCF · GPP · consent string · OpenRTB

    Contents

    Key facts

    Jump to the table

    Definition

    Prebid Server user sync: the process that collects each bidder's own identifier for a browser and stores the bidder-to-ID map in a cookie on the Prebid Server host's domain. The server then passes the right ID to each bidder in OpenRTB user.buyeruid (Prebid.org, User ID Sync, accessed 9 November 2026). Prebid's documentation uses "cookie sync" and "user ID sync" for the same mechanism.

    Why server-side bidding needs a sync step

    In a browser auction, each bidder's request leaves the reader's browser, so the bidder reads its own cookie. In a server-side auction, Prebid Server calls the bidders from a data centre. No bidder cookie travels with that call. Without a sync, every server-side bid request arrives anonymous, and many buyers bid less or not at all.

    The sync gives Prebid Server a lookup table instead. Prebid's own guide states the goal: bidder cookies "must be consolidated under the Prebid Server domain" so they can be sent with auction calls (User ID Sync). The wider trade-off between browser and server auctions is covered in header bidding explained and in our header bidding vs server-side bidding comparison.

    Take a news site that moves six bidders to the server. On a reader's first visit, Prebid Server holds no IDs for that browser. The first page view triggers syncs. From the second page view on, each synced bidder sees its own ID in the bid request.

    How a sync runs, step by step

    1. Prebid.js asks for syncs. When s2sConfig is set, the pbsBidAdapter posts the list of server-side bidders and the privacy signals to /cookie_sync (User ID Sync). The client side is covered in our Prebid.js guide.
    2. Prebid Server picks bidders. It starts with the bidders list, or all known bidders if none is sent. It checks the uids cookie, applies limit, fills spare slots with other bidders if coopSync is true, then applies filterSettings (/cookie_sync docs).
    3. The browser fires pixels. Prebid.js drops an image for each redirect sync and an iframe for each iframe sync.
    4. The bidder reads its cookie. The bidder's sync endpoint reads its own cookie, creating an ID if none exists, and redirects to Prebid Server's /setuid with the ID in the URL.
    5. Prebid Server writes the cookie. /setuid checks privacy again and, if allowed, adds the ID to the uids cookie (/setuid docs).
    6. The next auction uses it. On a later /openrtb2/auction call, Prebid Server reads the cookie and puts each bidder's ID in user.buyeruid for that bidder only.

    This is a request as Prebid.js or a test client might send it. Every field is optional, and all of them can go in the POST body (/cookie_sync docs).

    {
      "bidders": ["pubmatic", "ix", "openx"],
      "gdpr": 1,
      "gdpr_consent": "TCF-STRING-FROM-YOUR-CMP",
      "gpp": "GPP-STRING-FROM-YOUR-CMP",
      "gpp_sid": "2",
      "limit": 3,
      "coopSync": false,
      "account": "1001",
      "filterSettings": {
        "iframe": { "bidders": "*", "filter": "exclude" }
      }
    }

    A typical response lists one entry per bidder that needs a sync. The client must turn each url into an image tag for redirect and an iframe for iframe (/cookie_sync docs).

    {
      "status": "ok",
      "bidder_status": [
        {
          "bidder": "pubmatic",
          "no_cookie": true,
          "usersync": {
            "url": "https://image8.pubmatic.com/AdServer/ImgSync?p=159706&gdpr=1&gdpr_consent=TCF-STRING&pu=https%3A%2F%2Fpbs.example.com%2Fsetuid%3Fbidder%3Dpubmatic%26uid%3D%23PMUID",
            "type": "redirect"
          }
        }
      ]
    }

    The sync URL template comes from the bidder's own configuration file. PubMatic's PBS-Go file, for example, declares a redirect URL whose user macro is #PMUID (pubmatic.yaml, accessed 9 November 2026). How those files are structured is covered in Prebid Server bidder adapters.

    What /setuid writes, and when it refuses

    /setuid takes bidder, uid, gdpr, gdpr_consent and a format flag f. With f=i it returns a 1x1 PNG; with f=b it returns an empty HTML body (/setuid docs). A missing uid deletes that bidder's entry.

    GET https://pbs.example.com/setuid?bidder=pubmatic&uid=7C5B3A69&gdpr=1&gdpr_consent=TCF-STRING&f=i

    It answers 200 on success, 400 when the request is in GDPR scope and the consent string is missing or invalid, and 451 when privacy rules forbid the cookie (/setuid docs). A 451 is not an error to fix in code. It means the consent check worked.

    Decoded, the cookie is plain JSON. PBS-Go stores it base64 URL-encoded under the name uids, with each ID under tempUIDs and its own expiry time (cookie.go). The IDs below are placeholders.

    {
      "tempUIDs": {
        "pubmatic": { "uid": "7C5B3A69-C579-4BB4", "expires": "2026-11-23T10:15:02Z" },
        "ix": { "uid": "XE8m2dHM6qQAAAXt", "expires": "2026-11-23T10:15:04Z" },
        "openx": { "uid": "f1f4ac13-99f8-46da", "expires": "2026-11-23T10:15:05Z" }
      }
    }

    Three timers matter here. The cookie itself expires after ttl_days, 90 by default. Each ID inside it counts as live for 14 days in PBS-Go, after which /cookie_sync asks the bidder again (cookie.go). And when the cookie grows past max_cookie_size_bytes, Prebid Server ejects IDs until it fits; values between 1 and 499 bytes are rejected at startup (config.go).

    /getuids returns the same map as {"buyeruids": {...}}. Prebid documents it for publishers whose own servers call Prebid Server and cannot read the cookie (/getuids docs).

    The two builds expose the same idea under different keys and defaults; their wider differences are in Prebid Server Go vs Java. The table uses the current master branches of both repositories, and account-level values can also live in the account data described in Prebid Server stored requests.

    Sources for the table: config.go, usersync.go, account.go, PBS-Go 4.7.0, application.yaml, config-app.md and PBS-Java 4.0.0, all accessed 9 November 2026.

    Here is a PBS-Go configuration that sets the cookie on a dedicated subdomain and turns on a bounded cooperative sync. Values marked "yours" are placeholders.

    # pbs.yaml (PBS-Go 4.11.x). Env vars use the PBS_ prefix, e.g. PBS_EXTERNAL_URL.
    external_url: "https://pbs.example.com"   # yours; /setuid redirects are built from this
    host_cookie:
      domain: "pbs.example.com"               # yours; empty means the request host
      ttl_days: 90                             # default 90; align with your GVL maxCookieAgeSeconds
      max_cookie_size_bytes: 4000              # default 0 (no cap); 1-499 fails at startup
    user_sync:
      coop_sync:
        default: true                          # default false in PBS-Go
    cookie_sync:
      disabled_iframe_bidders: []              # host-wide list; "*" blocks iframe syncs for all
    account_defaults:
      cookie_sync:
        default_limit: 6                       # used when the request sends no limit
        max_limit: 10                          # caps any limit the request sends
    gdpr:
      host_vendor_id: 0                        # yours: your IAB Europe GVL ID
      default_value: "1"                       # required; "1" treats unknown requests as in scope

    The same intent in PBS-Java, passed with --spring.config.additional-location:

    # prebid-config.yaml (PBS-Java 4.5.x)
    external-url: "https://pbs.example.com"   # yours
    host-cookie:
      domain: "pbs.example.com"               # yours
      ttl-days: 90
      max-cookie-size-bytes: 4096             # Java default; 0 disables the cap
    cookie-sync:
      default-limit: 6                        # Java default 8
      max-limit: 10
      coop-sync:
        default: true                         # Java default true
    gdpr:
      host-vendor-id: 0                       # yours: your IAB Europe GVL ID
      default-value: "1"

    Both blocks parse as YAML. Check every key against the version you deploy, because both projects say their configuration source files are the full reference (PBS-Go config.go; PBS-Java config-app.md).

    Privacy rules decide whether any sync happens

    Prebid Server tests TCF consent for its own host company before it syncs. Responding to /cookie_sync and setting the cookie on /setuid both need Purpose 1 (store or access information on a device) (Prebid Server privacy, accessed 9 November 2026). Without it, /cookie_sync returns no syncs and /setuid fails. Prebid's docs also say a host needs its own Global Vendor List ID for the cookie to be set in GDPR scope.

    PBS-Go adds a sharper rule at the endpoint. If gdpr is 1 and no consent string arrives, it rejects the call. If gdpr is missing and the host's default treats requests as in scope, it rejects the call too and tells the client to send gdpr=0 to opt out (cookie_sync.go). The broader EU picture is in GDPR, ePrivacy and the bidstream, and the signal plumbing in GPP and TCF implementation for engineers.

    The browser adds a second gate. Since Prebid.js 11.9.0 (29 April 2026), Prebid.js fires no bidder syncs in Firefox or Chrome on iOS, as in Safari. Since 11.20.0, it also skips the Prebid Server /cookie_sync call in those browsers (setConfig docs; 11.9.0 release). For that traffic, server-side bidders must rely on other signals, such as the ID modules discussed in identity after Privacy Sandbox.

    AMP pages run no Prebid.js, so nothing calls /cookie_sync by itself. The host places load-cookie.html on a CDN, and the publisher embeds it in an amp-iframe. The script calls /cookie_sync with max_sync_count, the bidder list and consent, and asks for image syncs only, because AMP does not support iframe syncs (User ID Sync). The script moved from the Prebid Universal Creative repository to the prebid/user-sync repository in July 2024.

    <amp-iframe width="1" height="1" title="User Sync"
      sandbox="allow-scripts allow-same-origin" frameborder="0"
      src="https://cdn.example.com/load-cookie.html?source=amp&endpoint=https%3A%2F%2Fpbs.example.com%2Fcookie_sync&max_sync_count=5">
      <amp-img layout="fill" placeholder src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="></amp-img>
    </amp-iframe>

    Mobile apps are different again. Prebid's FAQ says the Prebid SDK has no cookies, so no syncing takes place there (Prebid Server FAQ, accessed 9 November 2026). App traffic is identified through device and ID signals instead.

    The uids cookie lives on whichever domain serves Prebid Server (User ID Sync). With a managed host, that is the host's domain, and the bidder-to-ID map builds up there across all of the host's publishers. With your own deployment, it is a domain you choose through host_cookie.domain.

    That choice has knock-on effects. Cooperative sync makes a large shared host better at matching, because IDs collected on one site help on the next. A single-publisher deployment starts from zero and matches only its own readers. The commercial side of that trade-off is set out in the profiles of Magnite Prebid Server, PubMatic OpenWrap and self-hosted Prebid Server. The build path itself is in the Prebid Server self-hosting guide, and the wider case for owning the sell-side stack is on our publisher independence hub.

    Common mistakes

    1. Leaving external_url at its default. PBS-Go builds the /setuid redirect from external_url, which defaults to http://localhost:8000 (config.go). Every bidder then redirects browsers to localhost, and no cookie is ever written. Set it with the rest of the container settings shown in our AWS and Docker reference setup.
    2. No host GVL ID. PBS-Go logs a warning and skips host GDPR checks when gdpr.host_vendor_id is 0 (config.go). Prebid's privacy page says the cookie will not be set in GDPR scope without one. Settle this with counsel before launch.
    3. Copying Java sync URLs from old docs. Since PBS-Java 4.0.0, sync URLs use single braces such as {gdpr}. Templates with {{gdpr}} from older pages no longer resolve (PBS-Java 4.0.0).
    4. Expecting PBS-Go to cap syncs. With no limit and no account default, PBS-Go returns every bidder that needs a sync. Set default_limit so a first page view does not fire dozens of pixels.
    5. Treating 451 as a bug. A 451 from /setuid means the consent check refused the cookie. Measure it as a privacy outcome, not an error rate.
    6. Enabling every adapter with cooperative sync on. PBS-Go enables most bidders by default, so coop sync can return bidders you have no contract with. Prebid recommends disabling unused adapters for this reason (bidder hosting guidelines).
    7. Counting syncs in Safari. Prebid.js no longer calls /cookie_sync there. Segment match-rate reports by browser before you blame the server.

    To see the result of a sync, run an auction with "test": 1 and read ext.debug.httpcalls for the bidder's buyeruid. The full debugging workflow is in Prebid Server analytics and debugging.

    FAQ

    What is cookie sync in Prebid Server?
    It is the round trip that stores each bidder's user ID in a uids cookie on the Prebid Server domain. Prebid.js calls /cookie_sync, each bidder redirects the browser to /setuid, and later auctions pass the stored ID to that bidder in user.buyeruid (User ID Sync).
    Why is my Prebid Server match rate low?
    Check four things in order: consent (Purpose 1 for the host), browser mix (Safari and iOS browsers no longer sync), the sync limit, and whether external_url points at a public HTTPS host. Prebid's FAQ adds that EEA-heavy traffic and small userSyncLimit values both slow matching (Prebid Server FAQ).
    Does Prebid Server cookie sync work in Safari?
    Not through Prebid.js. Since Prebid.js 11.20.0, released on 24 June 2026, the Prebid Server adapter skips /cookie_sync in Safari, in Firefox and Chrome on iOS, and when cookies are denied (setConfig docs).
    What is cooperative sync?
    It lets Prebid Server return sync URLs for bidders that are not on the current page, so their IDs are ready for the next one. The request flag is coopSync; the host default is true in PBS-Java and false in PBS-Go unless changed (/cookie_sync docs; application.yaml).
    How long does the uids cookie last?
    The cookie defaults to 90 days in both builds, set by host_cookie.ttl_days or host-cookie.ttl-days. Prebid advises matching it to the maxCookieAgeSeconds the host declares in the Global Vendor List (Prebid Server privacy).
    Does AMP support Prebid Server cookie sync?
    Yes, through the load-cookie.html page served in an amp-iframe. It calls /cookie_sync and returns image syncs only (User ID Sync).

    How this works on owned infrastructure

    The question on this page is who holds the map between your readers and each buyer's ID. With a rented Prebid Server, the uids cookie sits on the vendor's domain and the sync logs sit in the vendor's systems. AdSovereign runs the sell-side stack, Prebid Server included, on servers or a cloud account you control, so the cookie domain, the consent checks on /setuid and the sync records stay under your own configuration and retention rules. You still need a lawful basis and a GVL decision; the software does not settle either. The architecture page shows where the sync endpoints sit, and security covers how logs and keys are handled.

    Sources

    1. Prebid.org, "Prebid Server User ID Sync" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/developers/pbs-cookie-sync.html
    2. Prebid.org, "Prebid Server Endpoints: /cookie_sync" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-cookieSync.html
    3. Prebid.org, "Prebid Server Endpoints: /setuid" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-setuid.html
    4. Prebid.org, "Prebid Server Endpoints: /getuids" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-getuids.html
    5. Prebid.org, "Prebid Server Privacy Features" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-privacy.html
    6. Prebid.org, "Bidder-Specific Hosting Guidelines" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/hosting/bidder-specific-guidelines.html
    7. Prebid.org, "Prebid Server FAQ" (accessed 9 November 2026). https://docs.prebid.org/faq/prebid-server-faq.html
    8. Prebid.org, "pbjs.setConfig: Configure User Syncing" (accessed 9 November 2026). https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html
    9. GitHub, prebid/prebid-server, router/router.go, endpoints/cookie_sync.go, usersync/cookie.go, config/config.go, config/usersync.go, config/account.go, static/bidder-info/pubmatic.yaml (master, accessed 9 November 2026). https://github.com/prebid/prebid-server
    10. GitHub, prebid/prebid-server-java, src/main/resources/application.yaml and docs/config-app.md (master, accessed 9 November 2026). https://github.com/prebid/prebid-server-java
    More sources (3)
    1. GitHub, prebid/prebid-server releases v4.7.0 (20 July 2026) and v4.11.1 (2 November 2026). https://github.com/prebid/prebid-server/releases
    2. GitHub, prebid/prebid-server-java releases 4.0.0 (6 August 2026) and 4.5.0 (4 November 2026). https://github.com/prebid/prebid-server-java/releases
    3. GitHub, prebid/Prebid.js releases 11.9.0 (29 April 2026) and 11.20.0 (24 June 2026). https://github.com/prebid/Prebid.js/releases