ScreenshotNeo

BlogHow-to

How to Use Cookies to Skip Twitter Login in Puppeteer

Learn how Puppeteer stores cookies for authorized tests, why cookies do not guarantee an X login, and how to persist sessions safely.

By the ScreenshotNeo team1 October 20267 min read

Short answer: Puppeteer can read, set, and delete browser cookies, so you can persist session state between runs for an application you own or are authorized to test. Importing cookies does not guarantee that X (formerly Twitter) will accept a session, and scripting the X website is restricted by X’s current automation rules. Use the official X API for supported integrations.

This guide shows the safe, general technique with a local or user-owned test application. It does not provide X cookie names, copied session values, or instructions for bypassing another person’s login.

What cookies can and cannot do

Cookies are browser storage sent with matching requests. A site may use them to remember a session, preferences, security state, or a consent decision. X says cookies help keep users logged in and support authentication and security, but its documentation does not publish a stable cookie-import recipe that guarantees an authenticated X session.

Authentication may also depend on server-side state, device checks, expiry, other storage, or a fresh challenge. Therefore, treat a cookie file as test state, not as a portable login credential. Never export or share cookies from an account you do not own or lack direct authorization to test.

Policy before automating X

X’s Automation rules tell developers not to use non-API-based automation such as scripting the X website and warn that violations may result in permanent suspension. X’s Rules also restrict using cookies, credentials, tokens, or keys to access another person’s account without authorization.

Use the examples below with a local test app, a staging system, or another service whose owner has approved the automation. For a supported X integration, follow the official API and authorization documentation.

Current Puppeteer documentation exposes cookie operations at the browser level, with equivalent methods on BrowserContext. The page-level cookie methods used by older tutorials are deprecated, so check the API reference for the Puppeteer version installed in your project. The current CookieData reference displays version 25.12.0 and documents fields including name, domain, path, expires, httpOnly, secure, and sameSite.

Complete runnable example: save and restore an authorized test session

Create a project and install Puppeteer:

mkdir cookie-session-demo
cd cookie-session-demo
npm init -y
npm install puppeteer

Save this as session.js. Replace http://localhost:3000 with a test URL you control.

const fs = require('node:fs/promises');
const puppeteer = require('puppeteer');

const TEST_URL = process.env.TEST_URL || 'http://localhost:3000/account';
const COOKIE_FILE = process.env.COOKIE_FILE || './authorized-cookies.json';

async function saveSession(page) {
  // Browser-level APIs are preferred in current Puppeteer versions.
  const cookies = await page.browser().cookies();
  await fs.writeFile(COOKIE_FILE, JSON.stringify(cookies, null, 2), {
    mode: 0o600
  });
  console.log(`Saved ${cookies.length} cookies to ${COOKIE_FILE}`);
}

async function restoreSession(browser) {
  const raw = await fs.readFile(COOKIE_FILE, 'utf8');
  const cookies = JSON.parse(raw);
  if (!Array.isArray(cookies)) {
    throw new TypeError('Cookie file must contain a JSON array');
  }
  await browser.setCookie(...cookies);
  console.log(`Restored ${cookies.length} cookies`);
}

(async () => {
  const browser = await puppeteer.launch({headless: true});
  try {
    const page = await browser.newPage();
    await page.goto(TEST_URL, {waitUntil: 'networkidle2', timeout: 30_000});

    // Complete the approved login flow in your test environment here.
    // Example: await page.type('[name="email"]', process.env.TEST_EMAIL);
    // Example: await page.type('[name="password"]', process.env.TEST_PASSWORD);
    // Example: await page.click('button[type="submit"]');
    // Example: await page.waitForNavigation({waitUntil: 'networkidle2'});

    if (process.argv.includes('--save')) {
      await saveSession(page);
    }

    if (process.argv.includes('--restore')) {
      await restoreSession(browser);
      await page.reload({waitUntil: 'networkidle2'});
    }

    console.log('Final URL:', page.url());
    console.log('Title:', await page.title());
  } finally {
    await browser.close();
  }
})();

Run the approved login flow once and save the resulting state:

TEST_URL=http://localhost:3000/account node session.js --save

On a later run, restore the saved cookies before checking the protected page:

TEST_URL=http://localhost:3000/account node session.js --restore

The example writes the file with owner-only permissions. Keep it out of Git, CI logs, screenshots, and bug reports. A cookie file is credential material.

Set, inspect, and delete individual cookies

const browser = await puppeteer.launch({headless: true});
try {
  await browser.setCookie({
    name: 'test_flag',
    value: 'enabled',
    domain: 'localhost',
    path: '/',
    httpOnly: false,
    secure: false,
    sameSite: 'Lax'
  });
  const page = await browser.newPage();
  await page.goto('http://localhost:3000/', {waitUntil: 'networkidle2'});
} finally {
  await browser.close();
}

Read cookies for the current page

const cookies = await page.cookies();
console.table(cookies.map(({name, domain, path, expires, httpOnly, secure, sameSite}) => ({
  name, domain, path, expires, httpOnly, secure, sameSite
})));

Delete cookies

await browser.deleteCookie({
  name: 'test_flag',
  domain: 'localhost',
  path: '/'
});

Use the exact domain and path when deleting. A cookie for app.example.test is different from one for example.test, and a path-scoped cookie may not be removed by a broader path.

BrowserContext: isolate accounts and test state

Each BrowserContext has isolated cookies and local storage. Create one context per test account or scenario instead of mixing state in the default context.

const browser = await puppeteer.launch({headless: true});
try {
  const accountA = await browser.createBrowserContext();
  const accountB = await browser.createBrowserContext();

  const pageA = await accountA.newPage();
  const pageB = await accountB.newPage();

  await pageA.goto('http://localhost:3000/account-a');
  await pageB.goto('http://localhost:3000/account-b');

  console.log('A cookies:', await accountA.cookies());
  console.log('B cookies:', await accountB.cookies());

  await accountA.close();
  await accountB.close();
} finally {
  await browser.close();
}

Contexts are useful for parallel tests and account separation. They do not change a site’s automation policy and do not make scripting X’s website supported.

Field What to check
name and value Preserve exact strings; do not log values.
domain Must match the host scope. Host-only and parent-domain cookies differ.
path Controls which URLs receive the cookie.
expires Expired cookies will not restore a session.
httpOnly JavaScript cannot read it, but the browser can send it.
secure Sent only over HTTPS. Do not force secure cookies onto plain HTTP local tests.
sameSite Cross-site requests can be affected by Lax, Strict, or None.
  • Navigate to the cookie’s domain before setting a host-scoped cookie when your Puppeteer version requires an associated URL.
  • Do not assume cookies replace local storage, IndexedDB, service-worker state, or server-side sessions.
  • Handle consent and login redirects explicitly in tests; a successful setCookie call does not prove the application accepted the session.
  • Use a fresh context when a test must start unauthenticated.
  • Redact cookie values from thrown errors and CI output.

Troubleshooting

Symptom Likely cause Fix
Login page appears after restore Cookie expired, wrong domain/path, or the application needs other state. Capture cookies after the approved login, inspect non-secret metadata, verify expiry and scope, and reproduce the complete supported login flow.
Invalid cookie or protocol error Cookie JSON contains fields or types unsupported by your Puppeteer version. Compare the object with the installed version’s CookieData API reference; remove stale fields and use correct types.
Cookie is set but never sent secure, sameSite, domain, or path prevents matching. Use HTTPS for secure cookies, verify the request host and path, and check browser network events.
Tests affect one another Shared default context or reused profile. Create and close a separate BrowserContext per account or scenario.
Session works locally but fails in CI Different hostname, clock, environment variables, or expired state. Use a disposable test account, generate state in CI, avoid committing cookie files, and check system time.
Automation is blocked or an account is challenged Site policy or anti-abuse controls. Stop attempting to bypass the control. Use the site’s approved API or authorization path.

Performance, reliability, and cost

Restoring a small cookie jar is usually cheaper than repeating a multi-step login, but the dominant cost is browser startup and page navigation. Reuse a browser process for related tests, create isolated contexts instead of launching a browser per test, and close contexts promptly. Keep saved state short-lived and regenerate it when the application changes its session format.

For reliable tests, assert an application-owned signal such as a profile heading or authenticated API response rather than assuming that a cookie exists. Record status, redirect URL, and cookie metadata without recording secrets. Retries should be limited and should not repeat a login indefinitely when the service is challenging automation.

Or skip the browser setup

If your goal is a clean screenshot of a page rather than an authorized browser test, ScreenshotNeo provides a single HTTP request. Its capture pipeline accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API docs for the complete option list.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Does setting cookies guarantee that I am logged in?

No. The server may require additional storage, device checks, current expiry, or a supported authorization flow.

Can I use this to log in to someone else’s X account?

No. Do not use another person’s cookies or credentials. Restrict tests to accounts and systems you own or are directly authorized to test.

Should I use page.setCookie() from an old tutorial?

Check your installed Puppeteer version. Current guidance favors browser or BrowserContext cookie methods, and page-level methods are deprecated.

Will a separate BrowserContext bypass X’s automation rules?

No. Context isolation separates test storage; it does not make scripting the X website an approved integration.

How should supported X integrations authenticate?

Use X’s official API and its documented authorization mechanisms instead of automating the consumer website.

Primary references