DEV Community

StockRush
StockRush

Posted on

How to convert Netscape cookies.txt to JSON (and why your converter drops HttpOnly cookies)

If you've ever exported cookies with curl -c, yt-dlp, a browser extension or an antidetect browser, you've met cookies.txt β€” the Netscape cookie format. And if you've tried to import that file into something that expects JSON (Cookie-Editor, EditThisCookie, Puppeteer, Playwright, most antidetect browsers), you've probably had to convert it.

Here's how the format works, the one bug almost every quick converter has, and a tiny converter that gets it right.

The Netscape format in 30 seconds

One cookie per line, 7 fields separated by tabs:

.example.com    TRUE    /   TRUE    1893456000  sid abc123
Enter fullscreen mode Exit fullscreen mode
# Field Example Meaning
1 domain .example.com Cookie domain
2 include subdomains TRUE TRUE = sent to subdomains too
3 path / Cookie path
4 secure TRUE HTTPS only
5 expiration 1893456000 Unix time in seconds, 0 = session cookie
6 name sid
7 value abc123

Lines starting with # are comments. Almost.

The #HttpOnly_ trap

curl, yt-dlp and many browser exporters write HttpOnly cookies like this:

#HttpOnly_.example.com  TRUE    /   TRUE    1893456000  auth_token  secret
Enter fullscreen mode Exit fullscreen mode

It looks like a comment, so the classic one-liner converter skips it:

if (line.startsWith('#')) continue; // πŸ’₯ auth_token is gone
Enter fullscreen mode Exit fullscreen mode

The problem: HttpOnly cookies are usually exactly the ones you need β€” session and auth tokens. You import the "converted" file, and you're mysteriously logged out.

The fix is to check for the prefix before treating the line as a comment:

let httpOnly = false;
if (line.startsWith('#HttpOnly_')) {
  httpOnly = true;
  line = line.slice('#HttpOnly_'.length);
} else if (line.startsWith('#')) {
  continue;
}
Enter fullscreen mode Exit fullscreen mode

The JSON side

The JSON format that Cookie-Editor, EditThisCookie and most antidetect browsers import looks like this:

{
  "domain": ".example.com",
  "hostOnly": false,
  "path": "/",
  "secure": true,
  "httpOnly": true,
  "session": false,
  "name": "auth_token",
  "value": "secret",
  "expirationDate": 1893456000
}
Enter fullscreen mode Exit fullscreen mode

Two mappings people get wrong:

  • hostOnly is the inverse of "include subdomains". TRUE in the Netscape file means hostOnly: false.
  • Expiration 0 means a session cookie. Set session: true and leave out expirationDate instead of writing expirationDate: 0 (some importers treat that as "expired in 1970" and drop the cookie).

One more edge case: a cookie value can contain a tab. Split the line into fields and join everything after the 6th field back together instead of taking parts[6].

A converter that handles all of it

I put the whole thing into a single-file, zero-dependency converter: stockrush-org/netscape-to-json.

CLI:

npx github:stockrush-org/netscape-to-json cookies.txt > cookies.json

# and back
npx github:stockrush-org/netscape-to-json --reverse cookies.json > cookies.txt
Enter fullscreen mode Exit fullscreen mode

Node.js:

const { netscapeToJson, jsonToNetscape } = require('netscape-to-json');

const cookies = netscapeToJson(fs.readFileSync('cookies.txt', 'utf8'));
Enter fullscreen mode Exit fullscreen mode

Using it with Puppeteer:

const cookies = netscapeToJson(fs.readFileSync('cookies.txt', 'utf8'))
  .map(({ expirationDate, hostOnly, session, ...c }) => ({
    ...c,
    expires: expirationDate ?? -1,
  }));
await page.setCookie(...cookies);
Enter fullscreen mode Exit fullscreen mode

If you don't want to install anything, there's a free online version β€” it runs in the browser, nothing gets uploaded.

TL;DR

  • Netscape cookies.txt = 7 tab-separated fields per line.
  • #HttpOnly_ lines are cookies, not comments β€” don't skip them.
  • hostOnly = NOT "include subdomains"; expiration 0 = session cookie.
  • Join the value back if it contains tabs.

Found an edge case it doesn't handle? Issues and PRs are welcome.

Top comments (0)