Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To render a protected AngularJS route reliably, authenticate before navigation, wait for an application-owned “ready” signal, then add a controlled network-idle wait before calling page.pdf() or page.screenshot(). A networkidle event by itself is not proof that AngularJS has finished inserting data.

What a reliable export pipeline does

A protected single-page application has three separate timing problems: access, rendering, and capture. Puppeteer must reach the route with valid credentials, AngularJS must finish its requests and digest/render work, and the browser must use the media, viewport, fonts, and colors that match your desired output.

  1. Create an isolated browser context and keep secrets in environment variables or a secret manager.
  2. Set the viewport or device emulation before opening the route.
  3. Establish HTTP authentication, a session cookie, or the application’s login flow.
  4. Navigate with an explicit wait strategy and verify that the final URL is the protected route.
  5. Wait for an application-owned marker such as [data-export-ready="true"], then use network idle as a stabilization check.
  6. Capture a PDF or image, record diagnostics on failure, and close the page and browser only after the output promise resolves.

Prerequisites and application contract

  • Install a current Puppeteer package and use the Chromium revision it manages, or provide a compatible browser executable explicitly.
  • Have a test account or service account with permission to view the route.
  • Add a deterministic readiness marker to the AngularJS application. For example, set data-export-ready="true" on the report container only after required API calls, images, and calculations complete.
  • Know the cookie domain and path if you will restore a session. A cookie for app.example.com does not authenticate admin.example.com.
  • Decide whether the output should use print CSS (the Puppeteer PDF default) or screen CSS.

Puppeteer cannot infer business readiness. A route that has finished its initial navigation can still be showing a spinner while AngularJS fetches data.

Complete Node.js example: authenticated AngularJS PDF and screenshot

The following script supports HTTP Basic authentication, a pre-existing session cookie, an application readiness marker, and both PDF and PNG output. It expects PROTECTED_URL and EXPORT_DIR; add credentials only when your site requires them.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Canon PIXMA TS6520 Wireless Color Inkjet Printer, Duplex Printing, Copier/Scanner, 1.42" OLED Display, Compact, White
  • Affordable Versatility - A budget-friendly all-in-one printer perfect for both home users and hybrid workers, offering exceptional value
  • Crisp, Vibrant Prints - Experience impressive print quality for both documents and photos, thanks to its 2-cartridge hybrid ink system that delivers sharp text and vivid colors
  • Effortless Setup & Use - Get started quickly with easy setup for your smartphone or computer, so you can print, scan, and copy without delay
  • Reliable Wireless Connectivity - Enjoy stable and consistent connections with dual-band Wi-Fi (2.4GHz or 5GHz), ensuring smooth printing from anywhere in your home or office
  • Scan & Copy Handling - Utilize the device’s integrated scanner for efficient scanning and copying operations
const puppeteer = require('puppeteer');
const fs = require('fs/promises');
const path = require('path');

(async () => {
  const targetUrl = process.env.PROTECTED_URL;
  const exportDir = process.env.EXPORT_DIR || './exports';
  if (!targetUrl) throw new Error('Set PROTECTED_URL');
  await fs.mkdir(exportDir, { recursive: true });

  const browser = await puppeteer.launch({
    headless: true,
    // executablePath: process.env.CHROME_PATH, // optional managed browser override
    args: ['--no-sandbox'] // use only where your container policy requires it
  });
  const context = await browser.createBrowserContext();
  const page = await context.newPage();

  try {
    await page.setViewport({ width: 1440, height: 1000, deviceScaleFactor: 1 });

    // HTTP Basic authentication must be set before navigation.
    if (process.env.HTTP_USER && process.env.HTTP_PASSWORD) {
      await page.authenticate({
        username: process.env.HTTP_USER,
        password: process.env.HTTP_PASSWORD
      });
    }

    // Restore a session cookie before opening the protected route.
    if (process.env.SESSION_COOKIE) {
      await context.setCookie({
        name: 'session',
        value: process.env.SESSION_COOKIE,
        domain: new URL(targetUrl).hostname,
        path: '/',
        httpOnly: true,
        secure: true
      });
    }

    const response = await page.goto(targetUrl, {
      waitUntil: 'networkidle2',
      timeout: 90000
    });
    const status = response ? response.status() : null;
    const finalUrl = page.url();

    // A login redirect is an authentication failure, not a renderable report.
    if (//login(?:[/?]|$)/i.test(new URL(finalUrl).pathname)) {
      throw new Error(`Authentication redirect: ${finalUrl}`);
    }
    if (status && status >= 400) {
      throw new Error(`Initial HTTP status ${status} at ${finalUrl}`);
    }

    // This selector is an application contract, not a Puppeteer default.
    await page.waitForSelector('[data-export-ready="true"]', {
      visible: true,
      timeout: 60000
    });

    // Optional second marker for applications that expose a boolean flag.
    await page.waitForFunction(
      () => window.appReady === undefined || window.appReady === true,
      { timeout: 30000 }
    );

    await page.waitForNetworkIdle({ idleTime: 500, timeout: 30000 });
    await page.evaluate(() => document.fonts ? document.fonts.ready : Promise.resolve());
    await page.waitForSelector('[data-export-ready="true"] img', { timeout: 10000 }).catch(() => {});

    // Use screen CSS for visual parity. Remove this line to use print CSS.
    await page.emulateMediaType('screen');
    await page.addStyleTag({
      content: '* { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }'
    });

    await page.pdf({
      path: path.join(exportDir, 'report.pdf'),
      format: 'A4',
      printBackground: true,
      margin: { top: '16mm', right: '12mm', bottom: '16mm', left: '12mm' }
    });

    await page.screenshot({
      path: path.join(exportDir, 'report.png'),
      fullPage: true,
      type: 'png'
    });

    console.log(JSON.stringify({ ok: true, status, finalUrl }));
  } catch (error) {
    await page.screenshot({ path: path.join(exportDir, 'failure.png'), fullPage: true }).catch(() => {});
    console.error(JSON.stringify({ ok: false, url: page.url(), error: error.message }));
    process.exitCode = 1;
  } finally {
    await context.close();
    await browser.close();
  }
})();

The readiness selector must be changed to match your application. If the page contains a long-lived WebSocket, polling request, analytics beacon, or periodic refresh, network idle may never represent business readiness; keep the application marker as the primary gate and use network idle only as a short secondary delay.

Authentication methods

HTTP Basic authentication

Call page.authenticate({ username, password }) before page.goto(). Puppeteer notes that authentication turns on request interception, so avoid adding unrelated interception handlers that can block requests. Never print the password or embed it in source control.

Session-cookie authentication

Set the cookie on the browser context before navigation, with the exact domain, path, secure flag, and expiry behavior used by the application. Then verify both the final URL and a known authenticated element. A stale or incorrectly scoped cookie commonly produces a perfectly valid login page, which can be mistaken for a successful export.

Interactive login

Open the login page, fill the form, and coordinate the submit click with navigation:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const [loginResponse] = await Promise.all([
  page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 90000 }),
  page.click('button[type="submit"]')
]);
if (!loginResponse || loginResponse.status() >= 400) {
  throw new Error('Login navigation failed');
}
await page.waitForSelector('[data-authenticated="true"]', { timeout: 30000 });

If login is completed through XHR rather than a document navigation, wait for the application’s post-login marker or a specific API result instead of waiting forever for navigation.

Token or custom-header applications

Use the authentication mechanism supported by the application and keep tokens out of URLs, console output, screenshots, and error artifacts. The exact header setup differs by application; after applying it, verify the rendered DOM rather than assuming that a successful request alone means the page is authenticated.

Waiting for AngularJS data to finish

Prefer an application-owned marker

Set the marker only after all required calls and client-side transformations complete. A simple AngularJS controller or service can toggle the attribute after its promise chain resolves:

Rank #2
Sale
Canon PIXMA TS4320 – Wireless Color Inkjet Printer with Print, Copy, Scan
  • Affordable Versatility - A budget-friendly all-in-one printer perfect for both home users and hybrid workers, offering exceptional value
  • Crisp, Vibrant Prints - Experience impressive print quality for both documents and photos, thanks to its 2-cartridge hybrid ink system that delivers sharp text and vivid colors
  • Effortless Setup & Use - Get started quickly with easy setup for your smartphone or computer, so you can print, scan, and copy without delay
  • Reliable Wireless Connectivity - Enjoy stable and consistent connections with dual-band Wi-Fi (2.4GHz or 5GHz), ensuring smooth printing from anywhere in your home or office
  • Scan & Copy Handling - Utilize the device’s integrated scanner for efficient scanning and copying operations
$scope.loadReport().then(function (data) {
  $scope.report = data;
  $scope.exportReady = true;
  angular.element(document.querySelector('#report'))
    .attr('data-export-ready', 'true');
});

The marker should remain absent on API errors, authorization failures, and empty states that are not valid exports. You can use page.waitForFunction(() => window.appReady === true) instead when the application deliberately exposes a global flag.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use network idle as a bounded secondary wait

page.goto(..., { waitUntil: 'networkidle2' }) waits for a low number of active connections during navigation. page.waitForNetworkIdle({ idleTime: 500 }) adds a later stabilization period. Neither API knows whether a report’s business data is complete, and a continuously active application can prevent the promise from resolving. Set practical timeouts and fail with diagnostics.

Fonts, images, and lazy content

Await document.fonts.ready when typography affects pagination. For critical images, wait for a selector or check that each image has completed loading before capture. Full-page screenshots can trigger lazy loading as the page is measured, but a marker placed above the fold may still fire before lower sections are populated; include a marker that represents the entire export.

PDF output: media, pagination, and color

page.pdf() generates a PDF using the print CSS media type by default. This can hide navigation elements, change widths, or apply print-only page breaks. Call page.emulateMediaType('screen') first when the PDF must match the on-screen design.

  • Paper: choose format: 'A4', 'Letter', or explicit width and height.
  • Backgrounds: set printBackground: true; otherwise colored panels and background images may disappear.
  • Exact colors: add -webkit-print-color-adjust: exact (and the standard print-color-adjust) in the page’s CSS or an injected style.
  • Margins: specify them explicitly so headers, footers, and content do not collide.
  • Pagination: use print CSS such as break-inside: avoid for cards and tables, and test long reports at the target paper size.

Image output: viewport, full page, or one element

Set the viewport before navigation because AngularJS layouts can choose breakpoints during the first render. Use page.screenshot({ fullPage: true }) for the complete document. For a focused export, capture a rendered element:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const report = await page.locator('#report').screenshot({
  path: 'report-region.png',
  type: 'png'
});

If you need a retina-style image, increase deviceScaleFactor in page.setViewport. A larger scale increases memory and output size, so use it only when downstream consumers benefit from the extra pixels.

Coordinating clicks that navigate

A click can begin navigation before a separate wait is installed, creating a race and an intermittent timeout. Start both promises together:

Rank #3
HP OfficeJet Pro 8125e Wireless All-in-One Color Inkjet Printer, Print, scan, Copy, ADF, Duplex Printing Best-for-Home Office, 3 Month Instant Ink Trial Included, AI-Enabled (405T6A)
  • The OfficeJet Pro 8125e is perfect for home offices printing professional-quality color documents like business documents, reports, presentations and flyers. Print speeds up to 10 ppm color, 20 ppm black
  • PERFECTLY FORMATTED PRINTS WITH HP AI – Print web pages and emails with precision—no wasted pages or awkward layouts; HP AI easily removes unwanted content, so your prints are just the way you want
  • UPGRADED FEATURES – Fast color printing, scan, copy, auto 2-sided printing, auto document feeder, and a 225-sheet input tra
  • WIRELESS PRINTING – Stay connected with our most reliable dual-band Wi-Fi, which automatically detects and resolves connection issues
  • 3 MONTHS OF INSTANT INK WITH HP+ ACTIVATION – Subscribe to Instant Ink delivery service to get ink delivered directly to your door before you run out. After 3 months, monthly fee applies unless cancelled.
const [response] = await Promise.all([
  page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 90000 }),
  page.click('#continue')
]);
if (!response) throw new Error('No navigation response');
await page.waitForSelector('[data-export-ready="true"]', { timeout: 60000 });

For an AngularJS route change handled by the client router, there may be no document navigation. In that case wait for the destination URL, route-specific selector, or application marker instead.

Troubleshooting blank PDFs, login pages, and timeouts

Symptom Likely cause Fix
PDF contains the login form Credentials or cookie were applied to the wrong origin, expired, or set after navigation. Set authentication first; check the final URL and an authenticated-only selector before capture.
Blank PDF or image Capture ran before AngularJS inserted data, fonts, or images. Wait for the export marker, fonts, and critical images; save a failure screenshot and inspect the DOM.
Navigation timeout Long polling, WebSockets, analytics, or periodic refresh prevents the chosen idle condition. Use the application marker as the main gate, shorten the stabilization wait, and set a bounded timeout.
Colors or layout differ from the browser PDF uses print CSS by default, or backgrounds are disabled. Emulate screen, enable printBackground, and apply exact color adjustment.
Click intermittently hangs The click and navigation wait were started separately. Use Promise.all with waitForNavigation and click, or wait for a client-side route marker.
Images are missing below the fold Lazy loading has not been triggered or completed. Use a full-document readiness marker, wait for image completion, and allow a short post-render delay if needed.

Production reliability, security, and cost controls

  • Use a fresh browser context per tenant or user so cookies and local storage cannot leak between exports.
  • Keep credentials in environment variables or a secret-management system. Redact authorization headers and cookie values from logs.
  • Record the final URL, initial HTTP status, elapsed time, and a small diagnostic screenshot on failure. This distinguishes a login redirect from a rendering failure.
  • Close pages and contexts in a finally block, even when capture fails.
  • Bound navigation, readiness, network-idle, and capture timeouts separately. A single very long timeout hides failures and ties up workers.
  • Limit concurrent Chromium pages according to available memory; full-page screenshots, high device scale factors, and large PDFs require more memory.
  • Cache only when the underlying data is safe to reuse. For sensitive reports, disable shared caches and delete temporary files after delivery.
  • Run a representative route test whenever AngularJS templates, authentication, print CSS, or API contracts change.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup: ScreenshotNeo

ScreenshotNeo provides a website screenshot API and MCP server when you do not want to maintain Puppeteer authentication, browser binaries, and readiness plumbing. It accepts a URL with one GET request and returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and each response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

See the ScreenshotNeo documentation for parameter details. A minimal call is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

For protected routes, pass the supported authentication, cookie, header, wait, and output parameters described in the documentation. ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets and arbitrary viewports, retina scale, PDF paper size/margins/landscape/page ranges, HTML or CSS-to-image, custom JavaScript and CSS, pre-capture clicks, selector waits, delays, network-idle waits, ad/tracker/request/resource blocking, custom headers and cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs.

Plan Allowance and price
Free 1,000 shots per month, no card
Starter $5 for 3,000 shots
Growth $15 for 15,000 shots
Pro $39 for 60,000 shots
Scale $99 for 250,000 shots
Business $249 for 1,000,000 shots

Every feature is included on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

FAQ

Can Puppeteer reuse a login from another browser?

Yes, if you export a valid session cookie and restore it with the correct domain, path, security attributes, and expiry. Validate the session by checking the final URL and an authenticated-only DOM element before exporting.

Should I rely on networkidle2 for AngularJS?

No. It is a useful navigation or stabilization condition, but only your application can define when its required data and calculations are complete. Pair it with a deterministic marker or function exposed by the page.

Why does a PDF differ from a screenshot of the same route?

PDF generation selects print CSS by default and paginates content. A screenshot uses the emulated viewport and screen layout. Explicitly choose media, viewport, backgrounds, and print rules when comparing them.

How can I tell whether an export failed because of authentication?

Log the final URL and initial response status without logging secrets. A redirect to a login path or absence of a known authenticated marker indicates an access problem rather than an AngularJS rendering delay.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can Puppeteer reuse a login from another browser?

Yes, if you export a valid session cookie and restore it with the correct domain, path, security attributes, and expiry. Validate the session by checking the final URL and an authenticated-only DOM element before exporting.

Should I rely on networkidle2 for AngularJS?

No. It is a useful navigation or stabilization condition, but only your application can define when its required data and calculations are complete. Pair it with a deterministic marker or function exposed by the page.

Why does a PDF differ from a screenshot of the same route?

PDF generation selects print CSS by default and paginates content. A screenshot uses the emulated viewport and screen layout. Explicitly choose media, viewport, backgrounds, and print rules when comparing them.

How can I tell whether an export failed because of authentication?

Log the final URL and initial response status without logging secrets. A redirect to a login path or absence of a known authenticated marker indicates an access problem rather than an AngularJS rendering delay.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.