Screenshotting Pages Behind a Login: The Developer's Playbook
Learn how to screenshot behind login walls using session cookies, headers, and auth tokens — practical methods for dashboards, SaaS apps, and gated content.
Screenshotting a public webpage is trivial — point a headless browser at a URL and capture it. Screenshotting a page that requires a login is a different problem entirely. You need to somehow convince the rendering engine that it's already authenticated, without exposing credentials or breaking every time a session expires.
This comes up constantly for developers building things like:
- Automated visual regression tests for internal dashboards
- Client reporting tools that capture a customer's private analytics view
- Status pages or monitoring tools that check authenticated app states
- Link preview generators for internal wikis or gated documentation
Here's how it's actually done, with the tradeoffs of each approach.
Why This Is Harder Than Normal Screenshotting
A screenshot tool that just loads a URL will hit your login page, not your dashboard. To get past that, the browser instance taking the screenshot needs one of three things: valid session cookies, an auth header/token, or the ability to run a full login flow before capturing.
Each of these has different security implications and different failure modes, so picking the right one depends on how your app handles auth.
Method 1: Inject Session Cookies Directly
If your app uses cookie-based sessions (most traditional server-rendered apps and many SPAs do), this is the cleanest approach. Instead of logging in every time, you extract a valid session cookie once and pass it along with the screenshot request.
Steps
- Log in manually in a real browser and open DevTools → Application → Cookies.
- Copy the session cookie (commonly named
session,connect.sid,_session_id, or similar). - Pass that cookie to your screenshot API as a header on the request.
- Confirm the captured screenshot shows the authenticated view, not a login redirect.
With PxShot, this looks like a normal API call with a Cookie header attached:
GET https://api.pxshot.dev/screenshot?url=https://app.example.com/dashboard&cookie=session=abc123xyz
The rendering engine sends that cookie along with the initial request, so the target server treats it as an authenticated session before the page even starts loading — no login form ever appears in the render.
The Catch: Session Expiry
Session cookies expire. If your app has short-lived sessions (say, 1 hour), you can't hardcode a cookie and forget about it. Options:
- Use a long-lived "remember me" session if your app supports it
- Automate re-extraction of the cookie on a schedule (a small script that logs in and grabs the fresh cookie every N hours)
- Switch to token-based auth if your backend supports it (see Method 2)
Method 2: Pass an Authorization Header or API Token
If your app is a modern SPA that authenticates via JWT or a bearer token rather than cookies, you don't need session extraction at all — you just need a valid token.
This is often more stable than cookies because:
- Tokens can be issued specifically for automation/screenshot purposes
- You can scope a token to read-only access, reducing risk
- Expiry is predictable and can be set intentionally long for this use case
Example request with a custom header:
GET https://api.pxshot.dev/screenshot?url=https://app.example.com/reports/42
Authorization: Bearer eyJhbGciOi...
If your app checks the Authorization header on every request (common with API-driven frontends that hydrate client-side), the headless browser rendering the page will carry that header on the initial navigation and on any subsequent XHR/fetch calls the page makes — which matters if your dashboard loads data asynchronously after the initial HTML.
Method 3: Automate the Login Flow Itself
Sometimes there's no way around actually submitting the login form — for example, apps using CSRF tokens tied to a fresh page load, or multi-step auth (email + password + magic link confirmation).
In these cases, you need a screenshot workflow that:
- Navigates to the login page
- Fills in the username/password fields
- Submits the form
- Waits for the redirect to the authenticated page
- Then takes the screenshot
This is more fragile because any change to the login form's HTML structure breaks your automation. It's also slower, since you're paying the cost of a full login round-trip on every capture instead of reusing a session. Reserve this method for cases where cookie or token injection genuinely isn't possible.
Handling Pages That Load Auth-Gated Content Asynchronously
A common mistake: you authenticate correctly, but the screenshot still shows a blank dashboard or a loading spinner. This usually isn't an auth problem — it's a timing problem.
Many dashboards fetch their real data via client-side JavaScript after the initial page load. If your screenshot tool captures the page too early, you get the skeleton state instead of the populated one. Fixes:
- Add a delay parameter so the capture waits a few extra seconds after page load
- Wait for a specific selector to appear (e.g., wait until
.dashboard-chartis present in the DOM) rather than a fixed timer - Increase the timeout if the authenticated page is genuinely slow to hydrate
PxShot supports both a fixed delay and selector-based waiting, so you can tune capture timing to match how your specific app renders after login rather than guessing at a universal delay.
Security Practices You Shouldn't Skip
- Never hardcode real user credentials or session tokens in client-side code or public repos. Store them in environment variables or a secrets manager.
- Use a dedicated screenshot/service account where possible, rather than reusing a real employee or customer's session.
- Scope permissions down — if your app supports read-only API tokens, use those instead of full-access credentials.
- Rotate cookies/tokens regularly, especially if they're stored anywhere persistent like a cron job config or CI pipeline.
- Avoid screenshotting pages with sensitive PII unless the resulting images are stored with the same access controls as the original data.
Quick Decision Checklist
Before writing any automation code, figure out which bucket your app falls into:
- Cookie-based session, long expiry → extract cookie once, pass it on each screenshot request
- Cookie-based session, short expiry → automate cookie refresh on a schedule
- Token/JWT-based auth → pass a scoped bearer token via headers
- No stable session mechanism, CSRF-heavy login → automate the full login flow, accept the slower capture time
- Content loads after page render → add delay or wait-for-selector logic regardless of which auth method you use
Test with a single URL manually before wiring this into a cron job or CI pipeline — auth issues are far easier to debug interactively than after they've silently failed 200 times overnight.
If you want to try any of this without standing up your own headless browser infrastructure, PxShot supports cookie and header injection out of the box, plus delay and selector-wait options for asynchronous dashboards. The free tier is enough to test a real authenticated capture end-to-end before you commit to a workflow.