Access Secured Websites for Screenshot Capture in Java
Use Playwright Java to log in, save authenticated browser state, and capture protected pages. Learn cookie and HTTP auth options, security practices, and fixes.

To screenshot a page behind a login in Java, use a real browser automation library: sign in, wait until the site confirms authentication, save the browser context’s authenticated state, then open the protected URL in a context initialized from that state and capture it. Playwright Java supports this workflow directly. It is a good default when you need a repeatable capture job, because browser contexts isolate sessions and saved state can be reused. The example below is a template: replace its URLs and selectors with ones from the site you are authorized to access.
A login is not always just a cookie. Depending on the application, authenticated state may live in cookies, local storage, IndexedDB, or passkeys. Playwright storage state covers cookies and local storage, and can include IndexedDB when requested; it does not turn every possible authentication mechanism into a reusable file. See the Playwright authentication guide before choosing the state format. Treat the resulting file as a credential.
1. Set up Playwright Java
Use the Playwright Java dependency and browser installation process that match your project. The official Java introduction documents Maven setup, browser installation, and the supported Java APIs. With Maven, add the Playwright dependency to your project; install the browser binaries using the Playwright CLI described in that guide. The Java code below uses Playwright’s synchronous API and Chromium.
Keep credentials out of source code. The example reads the username and password from environment variables named SITE_USER and SITE_PASSWORD. Provide those variables through your local development environment or a secrets manager in production.
2. Log in, save state, and capture the protected page
This complete example logs in, waits for a post-login URL, writes storage state to disk, opens a fresh context with that state, and saves a full-page PNG. The login form labels, button name, URLs, and destination page are placeholders: adjust them to the application. The URL check is deliberately explicit so the capture does not race ahead while a redirect or authentication exchange is still in progress.

import com.microsoft.playwright.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class SecureScreenshot {
public static void main(String[] args) throws Exception {
String username = requiredEnv("SITE_USER");
String password = requiredEnv("SITE_PASSWORD");
Path authDir = Paths.get("playwright", ".auth");
Path stateFile = authDir.resolve("site.json");
Path outputFile = Paths.get("private-report.png");
Files.createDirectories(authDir);
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch();
try {
BrowserContext loginContext = browser.newContext();
try {
Page login = loginContext.newPage();
login.navigate("https://example.com/login");
login.getByLabel("Username").fill(username);
login.getByLabel("Password").fill(password);
login.getByRole(AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Sign in")).click();
// Replace with a stable URL or a reliable signed-in UI condition.
login.waitForURL("https://example.com/");
loginContext.storageState(
new BrowserContext.StorageStateOptions().setPath(stateFile));
} finally {
loginContext.close();
}
BrowserContext captureContext = browser.newContext(
new Browser.NewContextOptions().setStorageStatePath(stateFile));
try {
Page page = captureContext.newPage();
page.navigate("https://example.com/private/report");
page.locator("h1").waitFor(); // Replace with a page-specific ready marker.
page.screenshot(new Page.ScreenshotOptions()
.setPath(outputFile)
.setFullPage(true));
System.out.println("Saved " + outputFile.toAbsolutePath());
} finally {
captureContext.close();
}
} finally {
browser.close();
}
}
}
private static String requiredEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) {
throw new IllegalStateException("Set environment variable " + name);
}
return value;
}
}
Playwright’s locator-based interactions target accessible labels and roles, which are usually more robust than depending on a changing CSS class. If the site does not expose accessible names, use a selector that is stable for that application and check that it uniquely identifies the intended field or button.
3. Choose a reliable signed-in condition
After submitting credentials, do not assume that clicking the button means the session is ready. The browser may still be waiting for a redirect, a single-page application may update without changing URLs, or the site may display a second-factor challenge. Wait for evidence that the application considers the user signed in.
- Post-login URL: Use
waitForURLwhen successful login reliably navigates to a known destination. Match the site’s actual redirect behavior, including trailing slashes or query parameters. - Signed-in marker: Wait for a stable element such as an account menu, dashboard heading, or logout control with
page.getByRole(...).waitFor()or a locator assertion. This often works better when a single-page app keeps the same URL. - Protected destination: Navigate to the target and wait for a page-specific element. Also check for a login form or access-denied notice so the script does not silently save a screenshot of the wrong page.
- Second factor or consent: Handle any required challenge through an authorized flow. Do not treat a CAPTCHA or access-control challenge as a successful login. Some sites require a human or an approved integration for these steps.
Prefer a meaningful page marker over a fixed sleep. A delay can be useful for a known animation or delayed content, but it is not proof that authentication succeeded or that the page finished rendering.
4. Reuse saved state for later captures
Once a valid state file exists, later jobs can skip the interactive login and create a new context from it:

BrowserContext context = browser.newContext(
new Browser.NewContextOptions()
.setStorageStatePath(Paths.get("playwright", ".auth", "site.json")));
try {
Page page = context.newPage();
page.navigate("https://example.com/private/report");
page.locator("h1").waitFor();
byte[] png = page.screenshot();
// Send png to an authorized downstream store or processor.
} finally {
context.close();
}
Using a new context for each independent job helps prevent one capture’s cookies, local storage, or page state from leaking into another. Playwright describes browser contexts as independent sessions in its BrowserContext API reference. For a long-running worker, close each context when finished and close the browser during orderly shutdown.
If the app depends on IndexedDB, use the version of Playwright and the storage-state option documented for that version to include it when saving state. Test by loading the saved state into a fresh context and verifying the same signed-in marker. If the app uses a passkey or another browser-bound credential, a storage-state file alone may not be sufficient; use the application’s supported authentication or testing path.
5. Capture viewport, full page, an element, or bytes
Pick the screenshot scope based on the output you need. A viewport screenshot captures the currently visible browser area; a full-page capture extends through the page’s scrollable content; an element screenshot targets one component. Playwright’s screenshot guide documents these capture forms.
// Visible viewport, written to a file
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("viewport.png")));
// Entire scrollable page
page.screenshot(new Page.ScreenshotOptions()
.setPath(Paths.get("full-page.png"))
.setFullPage(true));
// A single component, after confirming it exists
page.locator(".invoice-summary").screenshot(
new Locator.ScreenshotOptions().setPath(Paths.get("summary.png")));
// Capture to memory for processing or upload
byte[] imageBytes = page.screenshot();
For an element screenshot, use a locator that identifies exactly one visible component and wait for it before capturing. Full-page screenshots can be very tall for feeds or dashboards; if the target has infinite scrolling, it may only load content that has been scrolled into view. You may need an application-specific scroll-and-load routine, or a viewport capture of the required section.
6. Other authentication methods
Inject a session cookie
If an approved login flow provides a session cookie, add it to a browser context before navigating to the protected page. Cookies are scoped by domain and may also require a path, expiry, secure flag, and same-site behavior. Install the cookie in the right context and verify the authenticated marker after navigation; a cookie copied from another environment may be expired or scoped to a different host.
BrowserContext context = browser.newContext();
context.addCookies(java.util.List.of(
new com.microsoft.playwright.options.Cookie("session", sessionValue)
.setDomain("example.com")
.setPath("/")
.setSecure(true)
.setHttpOnly(true)
));
Page page = context.newPage();
page.navigate("https://example.com/private/report");
Supply cookie values through a secret store, never by committing them into code. Use the exact cookie attributes required by the site. Playwright’s addCookies API documents accepted cookie fields.
HTTP Basic or Digest authentication
For a site protected by HTTP authentication rather than a form-based app login, configure credentials on the browser context. Playwright supports origin scoping and an option to send credentials without waiting for a challenge.
Browser.NewContextOptions options = new Browser.NewContextOptions()
.setHttpCredentials("user", "password");
BrowserContext context = browser.newContext(options);
Page page = context.newPage();
page.navigate("https://example.com/private/" );
When the site may prompt only on a particular origin, scope the credentials using the API’s origin option. Use the documented defaults for challenge behavior unless the server requires a different mode. The Playwright Browser API lists the credential configuration. Avoid sending credentials to unrelated origins.
Selenium WebDriver
If an existing Java project already uses Selenium, the same broad sequence applies: open the login page, submit credentials, wait for a confirmed authenticated state, then capture the target. Selenium’s cookie API can read and add cookies, subject to the current domain. See the Selenium Project’s Working with cookies documentation. For complex modern applications, account for local storage and app-specific state; copying cookies alone may not reproduce a Playwright-style browser session.
7. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its API takes a URL in one GET request; the example below saves a WebP response. See the ScreenshotNeo API documentation for request options and response behavior.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response includes X-Page-Verdict and X-Billed headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. The API captures a URL; it does not replace the authenticated browser workflow above for a private page that requires your own login session. Sign up for 1,000 free screenshots a month, with no card required.
8. Security checklist for authenticated captures
- Keep storage-state files outside source control; add the auth directory to
.gitignore. - Restrict file permissions and access to job logs, artifacts, temporary directories, and backups that could expose credentials or screenshots.
- Use dedicated, least-privilege accounts where the application supports them. Do not reuse a personal administrator session for unattended jobs.
- Read secrets from environment variables or a secrets manager. Avoid printing passwords, cookies, authorization headers, or storage-state contents.
- Set a retention policy: delete state that has expired or is no longer needed, and regenerate it after revocation or suspected exposure.
- Capture only pages and data you are authorized to access. Respect the site’s access rules and internal data-handling requirements.
Playwright warns that saved authentication state can contain cookies and headers capable of impersonating an account. Follow its guidance to keep the state file out of version control in the authentication guide.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The capture shows the login page | Login did not complete, state was not saved, or the target uses a different origin/session. | Wait for a signed-in marker after login; inspect the final URL; load the state in a fresh context and verify the marker before capture. |
| It works once, then fails later | The session expired, was revoked, or requires renewed authentication. | Regenerate state through the authorized login flow; handle expiry as a normal job outcome and avoid endless retries. |
| Cookies are present but the app still redirects | The session also relies on local storage, IndexedDB, CSRF state, or a different cookie scope. | Save the complete supported storage state from the logged-in context, check origin and cookie attributes, and confirm the app’s authentication design. |
waitForURL times out |
The site redirects to a different URL, stays on a single-page route, or rejected the login. | Inspect the final URL and visible error; wait for a stable signed-in element instead when the route does not change. |
| Element lookup times out | The selector or accessible name differs, the control is inside a frame, or the page has not rendered it. | Confirm the label and role in the actual page; wait for the right frame or use a stable application selector. |
| Screenshot is blank or incomplete | Capture began before content rendered, the target is hidden, or lazy content was never loaded. | Wait for a page-specific ready marker and visible target; scroll or trigger the application’s lazy loading before full-page capture. |
| Cookie injection has no effect | Cookie domain/path is incorrect, the value expired, or the site requires more than a cookie. | Use the exact scope and current authorized cookie; prefer storage state from a normal login when the app has multiple state components. |
| Browser launch fails | Playwright browser binaries are missing or incompatible with the installed library. | Install the browsers using the commands in the Java introduction for the project’s Playwright version. |
10. Performance, reliability, and cost
Browser automation starts a browser process and renders the page, so it generally uses more memory and setup than a direct HTTP request. Reuse a browser process across jobs when appropriate, but create a separate context for each independent authenticated session. This keeps session data isolated while avoiding needless browser launches. Close pages and contexts promptly, limit concurrent captures to available memory, and avoid downloading resources the page does not need when your capture requirements permit it.
For reliability, use explicit waits for the page state you need, bounded navigation and job timeouts, and a small retry policy for transient failures. Retrying a bad password or expired session will not fix authentication; classify that separately and refresh the state. Record useful diagnostics such as the final URL and failure category, but redact secrets and sensitive page content. Full-page images can consume substantially more memory and storage than a viewport shot, especially on long pages, so choose the smallest capture scope that serves the use case.
Playwright is open source; operational cost depends on the machine or hosted browser infrastructure where it runs, along with storage and any downstream processing. There is no fixed per-screenshot price in the cited Playwright documentation. For ScreenshotNeo, the stated plans are 1,000 free shots monthly, then Starter at $5 for 3,000, Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000; yearly billing gives two months free. Every feature is available on every plan. Choose based on the capture path, volume, and whether the pages require your authenticated browser session.
FAQ
Can I capture a private page without entering the password for every screenshot?
Yes, if the site allows a reusable authenticated session. Log in once, save browser state securely, and initialize later contexts from it. Refresh that state when the session expires.
Does a saved state file work across machines?
It may, but behavior depends on the site’s authentication and browser requirements. Transfer it only through a secure channel, protect it as a credential, and validate it in the target environment before relying on it.
Can I take the screenshot without saving it to disk?
Yes. Call page.screenshot() without a path to get image bytes, then pass them to the application’s authorized processing or storage code.
Is a cookie enough for every protected website?
No. Applications can use local storage, IndexedDB, passkeys, CSRF tokens, or additional checks. A cookie-only setup works only when that cookie is sufficient for the particular site and request context.


