Rendering Behind a Login

Rendering Behind a Login

How to screenshot pages that require you to be logged in, using session cookies or auth headers

Every Urlbox render runs in a fresh browser with no history, no cookies, and no saved sessions. Point it at a page that requires a login and you'll get a screenshot of the login form, not the content behind it.

To render the logged-in view, you need to give the render browser the same credentials your own browser presents. For most sites that means session cookies; for APIs and some apps it means an Authorization header.

How site logins work

When you log in to a site, the server sets one or more session cookies in your browser. On every subsequent request, your browser sends those cookies back, and that is what keeps you logged in - the server doesn't remember your browser, it recognises the cookies.

So the trick is:

  1. Figure out which cookie or cookies are actually keeping you logged in
  2. Grab their values from your browser
  3. Pass them to Urlbox with the cookie option

The render browser then sends the same cookies to the site, and the site serves it the same logged-in pages it would serve you.

Finding the session cookies

Open the site while logged in, then open your browser's DevTools and go to Application → Cookies (Chrome/Edge) or Storage → Cookies (Firefox) and select the site's domain.

Sites often set many cookies, and most of them are analytics or preferences, not authentication. Things that help narrow it down:

  • Session cookies usually have names like session, sessionid, sid, PHPSESSID, JSESSIONID, connect.sid, _<framework>_session, or __Secure-/__Host- prefixed names
  • They're usually marked HttpOnly and Secure
  • Their values are long opaque strings or tokens, not readable words

To confirm you've found the right ones, delete every other cookie for the domain in DevTools and reload: if you're still logged in, the cookies you kept are the ones you need. (Or do the reverse: delete a suspected session cookie and check that you get logged out.)

Passing cookies to Urlbox

Pass each cookie as a name=value string with the cookie option. Multiple cookies go in an array:

{
  "url": "https://example.com/account",
  "cookie": [
    "session_id=eyJhbGciOiJIUzI1NiIs...",
    "csrf_token=b1946ac92492d234"
  ]
}

You can also set cookie attributes such as Domain, Path, HttpOnly and SameSite - for example session_id=abc123;Domain=.example.com if the site expects the cookie on subdomains too.

Urlbox will now be effectively logged in as the user those session cookies were created for.

The security trade-off (read this)

A session cookie is your login. Anyone who has it can act as that user until the session expires or is revoked - which makes sharing session cookies with any third party over the internet essentially similar to sharing your password. Before doing this, understand what you're accepting:

  • Use a dedicated account. Create a separate account with the minimum access needed for the pages you want to render, and share that account's session, never your own.
  • Send cookies in a POST body, not a URL. Query-string render links containing session cookies can end up in browser history, server logs, and analytics. Use JSON POST requests to the API for anything sensitive - or better, keep the cookies out of the request entirely with default project options (next section).
  • Sessions usually expire - but don't rely on it. Most sites expire sessions after some period, which limits the damage window. But some setups never do: sites that put a JWT in a cookie and don't track sessions server-side often have no way to revoke a token at all - logging out in your own browser deletes your copy of the cookie but does nothing to the copy you shared. Check how the site behaves before assuming expiry protects you.
  • Rotate deliberately. When you stop using a session for rendering, log that account out everywhere (most sites invalidate the session server-side) or change its password.

Keep the credentials out of the request entirely

Rather than sending session cookies with every request, you can store them once as Default Project Options in your project settings. Default options are applied to every request made with that project's API key, and request-specific options override them - see the projects docs for how they work.

For logins this has a real security benefit: the sensitive values never appear in the request at all - not in a render link's query string, and not in the JSON you POST - so they can't leak through browser history, server logs, or the pages that embed your render links. Urlbox encrypts the stored values and adds them to the request server-side when it receives it.

This is the recommended home for cookie, header and authorization values: put the session cookie in the project's default options, and your actual requests stay free of secrets.

Alternatives to session cookies

Authorization header. APIs, and some apps, authenticate with an Authorization header instead of cookies. The authorization option sets it directly:

{
  "url": "https://api.example.com/dashboard",
  "authorization": "Bearer my_bearer_token"
}

This also handles pages protected by HTTP Basic auth ("authorization": "Basic base64credentials"). For sites that use a custom auth header such as X-API-Key, use the header option instead.

Tokens in localStorage or sessionStorage. Some single-page apps keep the auth token in web storage rather than a cookie (not a pattern we'd recommend building, since any JavaScript on the page can read the token, but plenty of sites do it). There's no direct Urlbox option for seeding web storage, and injecting it with the js option is fragile because your script runs after the app has already booted unauthenticated. In practice these apps usually send the stored token as an Authorization header on their API calls, so extracting the token from Application → Local Storage in DevTools and passing it via the authorization option often works. If it doesn't for your site, get in touch and we'll help.

Detecting expired sessions

When the session eventually expires, your renders won't error - they'll quietly start capturing the login page instead. Make that failure loud: pass a selector that only exists when logged in as wait_for with fail_if_selector_missing:

{
  "url": "https://example.com/account",
  "cookie": ["session_id=eyJhbGciOiJIUzI1NiIs..."],
  "wait_for": "#account-menu",
  "fail_if_selector_missing": true
}

Now an expired session fails the render with a clear error (and a render.failed webhook event if you use webhooks), telling you it's time to refresh the cookies, instead of silently filling your storage with screenshots of a login form.