Skip to content

Keep logins across Playwright runs with a persistent context and a static IP

To stay logged in across Playwright runs, save the session in a persistent profile (launch_persistent_context with a user data directory, or storageState) and launch it behind the same proxy IP every time. Sites often treat a saved cookie arriving from a new address as suspicious and ask for a fresh login. Stat Proxies static ISP IPs never rotate, cost $2.50 per IP per month with unlimited bandwidth, and have a 25 IP minimum.

At a glance

Recommended
Static ISP Proxies
Client
Playwright (Python or Node), Chromium
State kept
Profile directory or storageState JSON
Pairing rule
One profile, one proxy IP, always
Bandwidth
Unmetered on every plan
Starting at
$2.50 / proxy / mo

Why saved logins stop working.

A saved session is a cookie plus the context it was created in. If the context changes between runs, the site has a reason to ask again.

  1. Some sites tie a session to the address it was created from. OWASP's session guidance lists the client IP as a property you can bind a session ID to, and notes it is an imperfect check, so behavior varies by site. A login saved on one IP and replayed from another is the case most likely to be challenged.
  2. A fresh exit IP on every run changes that one thing you cannot control from inside the browser. Rotating pools make the mismatch the default, and static addresses remove it.
  3. storageState keeps cookies, localStorage and IndexedDB, but Playwright has no API to persist sessionStorage. If the app keeps its token there, the restored context starts logged out even with a perfect cookie file.
  4. A persistent profile directory can only be open in one browser at a time, and closing the context closes the browser. Two workers pointed at the same directory will fail or corrupt each other's state.
  5. Sessions expire server side whatever you save. Idle and absolute timeouts are enforced by the site, so a profile that sat unused for weeks may need a fresh login even on the right IP.

A setup that holds up.

Treat the profile and the IP as one unit. Create them together, keep them together, and never share either.

  1. Give each profile its own static IP: Assign one dedicated US ISP IP to each identity and write the pairing down (profile name to IP). The address your login was created from is then the address every later run uses.
  2. Use launch_persistent_context for the browser state: Point it at a dedicated folder per identity, never your everyday Chrome profile, and pass the proxy in the same call. You get cookies, localStorage and the rest of the profile back on every launch.
  3. Log in once, then check you are still in: Sign in the first time (by hand in headed mode if the site wants a second factor), then on later runs load a page that only a signed-in user sees and check for it before doing any work.
  4. Use storageState when you only need the cookies: For parallel workers or test suites, export storageState once and load it into fresh contexts, each launched through the matching proxy. Add sessionStorage by hand if the app needs it, and keep the file out of version control since it holds live credentials.
  5. Re-login on purpose, and replace a burned IP through the API: When the session is rejected, re-authenticate from the same IP. If an IP itself is blocked, swap it through the management API and treat that identity as a new login.
Python (Playwright, persistent profile)
import os
from pathlib import Path

from playwright.sync_api import sync_playwright

# One profile directory per identity, paired with one static Stat proxy IP.
PROFILE_DIR = Path("profiles") / "account-1"
PROXY = {
    "server": os.environ["STAT_PROXY_SERVER"],  # http://<proxy-ip>:<port>
    "username": os.environ["STAT_PROXY_USERNAME"],
    "password": os.environ["STAT_PROXY_PASSWORD"],
}
CHECK_URL = "https://<site>/account"  # a page only a signed-in user can load

with sync_playwright() as p:
    context = p.chromium.launch_persistent_context(
        user_data_dir=str(PROFILE_DIR),
        headless=False,
        proxy=PROXY,
    )
    page = context.pages[0] if context.pages else context.new_page()

    # Confirm the exit IP is the one this profile was created on.
    page.goto("https://api.ipify.org")
    print("exit ip:", page.inner_text("body"))

    page.goto(CHECK_URL)
    if "login" in page.url:
        input("Not signed in. Log in in the window, then press Enter...")
    else:
        print("Session restored")

    context.close()  # closing the context closes the browser

Keep credentials in separate fields as shown. The first run is a manual login; later runs reuse the folder. Replace the check URL and the login test with whatever your site actually uses.

How many IPs to start with.

Starting points, not limits. The rule is one IP per identity that holds a login.

A handful of saved logins
25 IPs (the plan minimum)
A fleet of agents or accounts
One IP per profile, 50 to 200 IPs
Large managed deployments
A private subnet

When this is the wrong fit

If you only run stateless requests and never log in, persistent profiles add nothing. And if the target also enforces extra checks such as device approval or a one-time code on every login, a stable IP lowers how often it appears but will not remove it.

Static ISP Proxies · Pricing · Contact

Frequently asked questions

Does launch_persistent_context work with a proxy?

Yes. It accepts the same proxy option as a normal launch, with server, username and password as separate fields. The proxy is fixed for that browser, so pass the same one on every launch of a given profile. To change it, close the context and launch again.

Should I use storageState or a persistent context?

Use a persistent context when one identity owns one long-lived browser profile. Use storageState when you want to export a login once and load it into many fresh contexts. Playwright's storageState does not persist sessionStorage, so check where your app keeps its token.

Why does Playwright log me out when my IP changes?

Some sites bind a session to the address it was created from, or flag a known cookie arriving from somewhere new. Not every site does, so test yours. If yours does, a static IP paired with the profile removes that variable.

Can two Playwright workers share one profile folder?

No. A user data directory can only be used by one browser instance at a time. Give each worker its own folder and its own proxy IP.

How much does a static IP per profile cost?

Static ISP proxies are $2.50 per IP per month with unlimited bandwidth and a 25 IP minimum, so the smallest plan is $62.50 a month. Because bandwidth is not metered, heavy pages in a long-lived logged-in browser do not raise the bill.

Is it allowed to automate a logged-in session?

That depends on the site's terms of use, which often restrict automated access, and on what you do once logged in. Use accounts you own or are authorized to operate, read the terms, and get legal advice for commercial use. Stat Proxies provides the network connection only.

Related