What is the _px3 cookie?

Last reviewed 10 November 2026 · 7 min read

Short answer: _px3 is the cookie PerimeterX (now HUMAN Security) issues to a browser whose sensor check passed. It carries a payload encrypted against a key the site holds, a lifetime enforced inside that payload, and an HMAC signature over the payload. It is not a session token you can copy to another client: the signature covers what the payload says about your IP and user agent, and the edge re-signs on a schedule of its own.

This page is organised around the three things that actually decide whether your requests pass: what the fields mean, which clock is running, and which identity the cookie is pinned to. If the cookie is in your jar and the page is still a challenge, the answer is almost always one of those three.

What is actually inside it

A _px3 value is colon-separated: the encrypted payload, a timestamp, a hash and a signature. Practically, what matters is:

PartWhat it decides
Encrypted payloadContains the score PerimeterX calculated for the session, the IP and user agent it was issued to, and a UUID that ties it to _pxvid.
TimestampWhen it was issued. Site policy decides how long it is considered fresh.
SignatureHMAC over the payload with the site's own key. Altering the payload breaks it, so you cannot edit a low score into a high one.

The field count has changed across deployments — older values carry three colon-separated fields, newer ones four, with the extra field holding version metadata. Parse by splitting on : and taking the signature from the last field, not from a fixed index, or your code will break quietly the day the format moves.

The two expiries

This is where most implementations break. There is the Expires the server sets on the cookie, and there is a lifetime baked into the payload that the server enforces on its own. They do not have to agree. A cookie can be sent back to you with an Expires two hours out while the payload was already close to expiring, because the server's own TTL is measured from issuance, not from the last time you used it.

ClockWhere it livesMeasured fromWhat it causes
Cookie Expires / Max-AgeThe Set-Cookie headerThe last writeWhen the browser drops the value. Usually later than the payload dies, which is why this attribute is a trap.
Payload TTLInside the signed payloadIssuanceThe edge starts challenging while your jar still looks healthy.
Re-signAny response that carries Set-CookieWhenever the edge decidesThe value you hold is superseded. Sending a stale copy is itself a signal.

If you reuse a cookie until the browser would drop it, you spend the last part of that window getting challenge responses on a cookie that still looks valid in your jar. Reading Set-Cookie on every response and replacing the jar entry unconditionally is the fix — not parsing the Expires attribute.

Why copying it between machines fails

What it looks like in code

Two things are worth writing down: the shape that earns the cookie, and the way you decide it is still fresh. Neither involves decoding the payload.

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    ctx = browser.new_context(proxy={"server": PROXY}, locale="en-US")
    page = ctx.new_page()
    page.goto(url, wait_until="networkidle")   # the sensor runs here

    jar = {c["name"]: c["value"] for c in ctx.cookies()}
    print(sorted(jar))                         # expect _px3, _pxvid, _pxhd

    for attempt in range(3):
        r = page.request.get(url)              # same context: cookies, UA and TLS
        if r.status == 200:
            break

    # Never lift jar out of this context. The exit IP, user agent and TLS
    # stack are part of the pass, not decoration around it.
import time

def px3_age(value: str) -> float:
    parts = value.split(":")
    # 3 fields on older deployments, 4 on newer; the timestamp sits at index 1 either way.
    return time.time() - int(parts[1])

age = px3_age(jar["_px3"])
print("signed", round(age / 60, 1), "minutes ago")
if age > REFRESH_BUDGET:      # your own budget, not the cookie's Expires
    refresh_session()          # new browser context, same exit IP

Reading a 403 that arrives with a valid cookie

Most "the cookie is there but the page is blocked" reports collapse into five causes. Check them in this order, because the first two account for most of them:

What you seeWhat it usually meansFirst thing to change
200 once, then a challenge on the next request from the same jarThe payload TTL is measured from issuance, not from last useReplace the jar entry on every Set-Cookie; stop waiting for Expires
Challenge immediately after a proxy rotationThe payload records the IP it was issued toPin one exit IP per session; rotate sessions, not requests
Challenge after replaying a browser jar from requestsThe TLS fingerprint no longer agrees with the user agent in the payloadKeep the client stack that earned the cookie, or re-earn it in the new stack
_px3 present, _pxvid missingThe edge cannot resolve the session the signature refers toRestore the full jar rather than the one cookie you care about
Fine for minutes, then challenges under loadThe session is re-scored on machine-speed traffic at the next re-signLower the rate, or take a fresh value per session

Between websites and the s2s path

PerimeterX has a server-to-server token flow used by some integrations. That path returns a token verified directly with HUMAN's API and does not require a browser-side cookie at all. If a site you are authorised to test supports it, it is far more stable than a cookie jar — see the PerimeterX endpoint for how the SolveHub API exposes both modes.

Where s2s is not offered, the honest options are: run the sensor in a real browser and keep the whole jar plus network identity, or obtain a valid _px3 for a given URL from a solving API. Regenerating the payload yourself means reimplementing an obfuscated, rotating script and the site's key — not a maintenance burden worth taking on.

Related cookies

FAQ

Can I decode the _px3 payload? Not usefully. It is encrypted with a key the site controls, so you can see its length, not its contents. There is no client-side key.

Why does my _px3 stop working before it expires? Because the server enforces its own lifetime from issuance, which can be shorter than the cookie's Expires attribute. Replace the jar entry on every Set-Cookie response.

Does _px3 work from a different IP? Usually not for long. The payload records the IP and user agent of the issuing session, and a session evaluated under machine-speed traffic can be re-scored at the next re-sign.

Do I need _pxvid as well, or is _px3 enough? On most properties you need both. _px3 alone is a signature over a session the edge has no record of.

Can I raise the score by editing the cookie? No. The signature covers the payload, so any edit invalidates it. The score is computed from session behaviour, which is why the practical answer is a real browser or a fresh value from a solving API.