ScreenshotNeo

BlogHow-to

How to Capture a Page That Requires Login with HTMLCSStoImage

HTMLCSStoImage can render an authorized page with a session cookie or token, but it does not perform interactive login. Here’s the secure workflow, working code, and troubleshooting guide.

By the ScreenshotNeo team4 October 20269 min read

Short answer: HTMLCSStoImage does not perform an interactive sign-in. For a page you are authorized to access, give its URL screenshot request a valid, short-lived session cookie or authorization token in the headers parameter. If the site provides an embed that exposes the content you need, use that instead: its HTML can be rendered without logging in. [HTMLCSStoImage URL-to-image guide](https://docs.htmlcsstoimage.com/getting-started/url-to-image/)

This guide covers the documented authenticated-page workflow, runnable requests in cURL, Python, and Node.js, how header forwarding works, capture options, errors, and an alternative using ScreenshotNeo. Use credentials only for accounts and pages you are authorized to access. A screenshot request does not bypass MFA, CAPTCHA, authorization rules, or an interactive login challenge.

1. Decide how the page should be accessed

  1. Check for an official embed. If the page owner provides an embed and it reveals the needed content, capture the embed HTML. This avoids sending a login credential with the screenshot request.
  2. Otherwise, use an authorized session credential. Obtain a valid session cookie or token through the site’s supported login flow, then pass it in the request’s headers object.
  3. Check where the page loads its content. If the document or its assets require credentials from another origin, allow only that exact origin and enable subrequest headers only when necessary.
  4. Choose the capture scope and output. Use a full-page image for the whole document, a selector for one element, or PDF when a document is needed.

HTMLCSStoImage uses HTTP Basic authentication for its API: the API ID is the username and API key is the password. Keep those API credentials and the target site’s session credential out of source control, client-side code, and shared logs. [API usage documentation](https://docs.htmlcsstoimage.com/getting-started/using-the-api/)

2. Send the authenticated screenshot request

The examples below use environment variables for secrets. Set HCTI_API_ID, HCTI_API_KEY, and SESSION_COOKIE in your local environment or secret manager. Replace the example URL with the authorized page. The example sends the session cookie only with the top-level request to the page’s origin; subrequests do not receive it unless explicitly enabled.

cURL

curl --request POST "https://hcti.io/v1/image" \
  --user "$HCTI_API_ID:$HCTI_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "url": "https://app.example.com/private/report",
    "headers": {
      "Cookie": "session=REPLACE_WITH_SHORT_LIVED_SESSION_VALUE"
    },
    "format": "png",
    "full_screen": true
  }'

The API returns image metadata including a generated image URL. Fetch that returned URL to save the image. Avoid printing the request body or credential-bearing headers in CI logs. The following requests show how to parse the response and download the generated image.

Python

import os
import requests

api_id = os.environ["HCTI_API_ID"]
api_key = os.environ["HCTI_API_KEY"]
session_cookie = os.environ["SESSION_COOKIE"]

payload = {
    "url": "https://app.example.com/private/report",
    "headers": {"Cookie": f"session={session_cookie}"},
    "format": "png",
    "full_screen": True,
}

response = requests.post(
    "https://hcti.io/v1/image",
    auth=(api_id, api_key),
    json=payload,
    timeout=60,
)
response.raise_for_status()
image_url = response.json()["url"]

image_response = requests.get(image_url, timeout=60)
image_response.raise_for_status()
with open("authenticated-page.png", "wb") as image_file:
    image_file.write(image_response.content)

print("Saved authenticated-page.png")

Node.js

const apiId = process.env.HCTI_API_ID;
const apiKey = process.env.HCTI_API_KEY;
const sessionCookie = process.env.SESSION_COOKIE;

if (!apiId || !apiKey || !sessionCookie) {
  throw new Error("Set HCTI_API_ID, HCTI_API_KEY, and SESSION_COOKIE");
}

const basicAuth = Buffer.from(`${apiId}:${apiKey}`).toString("base64");
const response = await fetch("https://hcti.io/v1/image", {
  method: "POST",
  headers: {
    Authorization: `Basic ${basicAuth}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://app.example.com/private/report",
    headers: { Cookie: `session=${sessionCookie}` },
    format: "png",
    full_screen: true,
  }),
});

if (!response.ok) {
  throw new Error(`Screenshot request failed: ${response.status} ${await response.text()}`);
}

const result = await response.json();
const imageResponse = await fetch(result.url);
if (!imageResponse.ok) {
  throw new Error(`Image download failed: ${imageResponse.status}`);
}
const fs = await import("node:fs/promises");
await fs.writeFile("authenticated-page.png", Buffer.from(await imageResponse.arrayBuffer()));

Keep the session value in a secret store and rotate or discard it according to the site’s session policy. Do not put credentials in a screenshot URL or signed render URL. HTMLCSStoImage warns that URL parameters can be retained in browser history, server and analytics logs, and referrer data; the request-body header mechanism is the documented way to send secrets. [Headers parameter documentation](https://docs.htmlcsstoimage.com/parameters/headers/)

3. Handle cross-origin redirects and page assets

By default, custom headers go to the requested URL’s origin on the top-level navigation. An origin includes scheme, hostname, and port, so https://app.example.com and https://api.example.com are different origins. A cross-origin destination receives the custom headers only when its exact origin is listed in additional_header_origins. CSS, image, JavaScript, and other subrequests receive the headers only when include_headers_on_subrequests is true. [Headers parameter documentation](https://docs.htmlcsstoimage.com/parameters/headers/)

Only add an origin you control or are explicitly authorized to use. Do not forward a session cookie broadly: an allowed origin can receive that credential on qualifying requests.

curl --request POST "https://hcti.io/v1/image" \
  --user "$HCTI_API_ID:$HCTI_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "url": "https://app.example.com/private/report",
    "headers": {
      "Authorization": "Bearer REPLACE_WITH_SHORT_LIVED_TOKEN"
    },
    "additional_header_origins": [
      "https://api.example.com"
    ],
    "include_headers_on_subrequests": true,
    "format": "png",
    "full_screen": true
  }'

This configuration is appropriate only if the page’s authorized API subrequests need the same bearer token. If only the main document needs it, omit both cross-origin settings. The documented origin list accepts exact HTTP or HTTPS origins without paths, query strings, fragments, credentials, or wildcards; it allows up to 20 unique origins. [Headers parameter documentation](https://docs.htmlcsstoimage.com/parameters/headers/)

4. Tune the capture

Once access works, select the smallest output that serves your use case. HTMLCSStoImage documents these URL screenshot options, among others. Consult its [full parameter reference](https://docs.htmlcsstoimage.com/parameters/) for accepted values and constraints.

Need Option Use
Entire document full_screen: true Captures the full page height.
One component selector: "#report" Crops to the matching CSS element. Confirm the selector exists after the page renders.
Specific output type format Supported documented choices include PNG, JPG, WebP, and PDF.
Viewport size viewport_width, viewport_height Set the browser viewport to the layout dimensions you need.
Higher pixel density device_scale Adjust resolution; higher values increase image dimensions and file size.
Long page output jumbo_max_width, jumbo_max_height Jumbo dimensions must be supplied together and consume additional image credits.

For a PDF, set format to pdf. For a focused capture, use selector instead of producing a very tall full-page image. If the page relies on lazy loading, delayed data, or client-side rendering, verify that the rendered output includes the content you need and consult the parameter documentation for wait behavior supported by your account and request type.

5. Troubleshoot common failures

Symptom Likely cause What to check
Login screen appears The cookie or token is missing, expired, malformed, or not valid for this host/path. Confirm the credential works in an authorized session and send the correct cookie name/value or bearer token in headers. Use a fresh, short-lived credential.
Interactive login or MFA prompt appears The page requires steps the URL renderer does not perform. Complete sign-in through the site’s supported flow, then use its authorized session credential, or use an official embed if available. The API does not automate interactive login.
Main page loads but images or data are missing Assets or API calls use a different origin, or subrequests need authentication headers. Identify the exact asset/API origin. Add only that origin to additional_header_origins, then enable include_headers_on_subrequests only if those subrequests need the credential.
Redirect ends at a login domain The destination is a different origin and did not receive the custom header. Check the redirect target. If authorized and required, list its exact origin; avoid wildcard forwarding.
Request rejected for invalid headers A header may be restricted, duplicated, malformed, oversized, or contain unsupported control characters. Use ordinary application headers only, remove duplicates, and check the headers parameter rules. Infrastructure-controlled and hop-by-hop headers cannot be overridden.
Blank or partially rendered capture The page may still be loading data, may be blocked by the destination, or may require browser-side interaction. Check the URL and access policy, ensure the content is available after ordinary page load, and tune documented wait/capture settings. A challenge or access restriction is not something the screenshot request bypasses.
API authentication fails The HTMLCSStoImage API ID/key pair is missing or incorrect. Use HTTP Basic authentication with API ID as username and API key as password; keep these separate from the target site’s session credential.
Allowlisted site rejects requests from the renderer HTMLCSStoImage says it does not provide a static source IP list because render servers scale dynamically. For a site you control that requires stable egress, the docs describe configuring an HTTP proxy and passing its proxy_id. This is an infrastructure configuration, not an authentication bypass.

6. Security, reliability, and cost considerations

  • Minimize credential scope and lifetime. Prefer a short-lived, narrowly scoped token or session. Do not use a personal long-lived credential when a limited service credential is available.
  • Limit where headers travel. Keep include_headers_on_subrequests false unless needed, and list only exact trusted origins. This reduces accidental credential exposure.
  • Protect both credential sets. The HTMLCSStoImage API key authenticates your API request; the cookie/token authenticates to the target site. Store both as secrets, redact them from logs, and do not commit them.
  • Expect session expiry and site changes. A previously valid cookie can stop working, and page markup or selectors can change. For recurring jobs, refresh credentials through the site’s authorized mechanism and handle failed captures explicitly.
  • Plan output size. Full-page and high-density captures can produce large images. A selector crop or a smaller device scale can reduce output size; jumbo mode uses additional image credits.
  • Check current billing behavior. The cited documentation explains that jumbo mode consumes additional image credits but does not establish a per-request price here. Review the provider’s current plan and credit terms before scheduling large or recurring jobs.

7. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. Its API accepts a URL in one GET request and supports cookies, custom headers, and Authorization. That can simplify a capture workflow when you already have an authorized credential; it still does not turn an interactive login challenge into an authorized session. See the ScreenshotNeo API documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.

FAQ

Can HTMLCSStoImage type a username and password into a login form?

No. The documented URL screenshot flow does not automate interactive login. Use an official embed or pass an authorized session cookie or token after completing the site’s sign-in flow.

No. Use the documented request-body headers parameter. URLs can be retained in histories and logs or appear in referrer data.

Why does the page work in my browser but not in the capture?

Your browser may have a current session, while the render request does not. Or the page may load authenticated assets from another origin. Pass an authorized credential and review the origin and subrequest rules.

Can a public embed show private account data?

Only if the site intentionally makes that content available through the embed. An embed is not a way to bypass the site’s access controls.

Does an IP allowlist solve a login problem?

No. An allowlist is an egress network condition, separate from authentication. HTMLCSStoImage documents proxy configuration for stable egress when needed; it does not provide a static renderer IP list.