Fix Website Screenshot APIs That Return a 403 Instead of the Page
Diagnose which layer returned a 403, check authorized credentials, and fix destination security rules safely when you control the site.
A 403 Forbidden means a server or security layer denied the request. It does not, by itself, mean the screenshot renderer failed to render the page. First determine whether the 403 came from the destination site, a proxy or security product, or the screenshot service; then use authorized credentials or correct the specific rule if you administer the site. A custom user-agent or longer wait cannot grant access.
This guide is for pages you are authorized to access. Do not try to defeat a site’s access controls. If you do not control its security policy, use an approved account or ask the owner or provider for an allowed way to capture it.
1. Find out what returned the 403
Before changing settings, record the requested URL, response status, headers, body, redirect chain (if exposed), and any request or trace identifier. Check whether the response is an image, an HTML login/challenge/error page, or a screenshot service’s own error object. Screenshot providers expose different diagnostics, so do not assume a response from one service describes another.
For Cloudflare specifically, an unbranded 403 generally comes from the origin; a Cloudflare-branded 403 can be produced by Cloudflare security features. The status alone does not identify the rule or layer. Inspect the response and, if you administer the site, the relevant security events. See [Cloudflare’s 403 guide](https://developers.cloudflare.com/support/troubleshooting/http-status-codes/4xx-client-error/error-403/).
| What you observe | Likely next check |
|---|---|
| HTML login page or application error page | Check whether the target requires a signed-in session or app-level permission. |
| HTTP Basic Authentication challenge | Use the renderer’s documented Basic Authentication option with authorized credentials. |
| Bearer-token or other authorization error | Check the expected header name, token format, scope, and whether the token is valid for that URL. |
| Branded security block page | Identify the security product and matching event; if you own the site, inspect its logs. |
| Image of an error page | The renderer may have successfully captured the page it received. Inspect the target response separately from the screenshot response. |
| 403 returned directly by the screenshot API | Check that service’s API authentication, endpoint, and documented error response, as well as any destination status details it provides. |
Some services note that a 401 or 403 may mean the captured image shows a login or error page. Treat that as a provider-specific diagnostic, not a universal behavior. See [Screenshot API’s documentation](https://screenshot-api.net/docs).
2. Check whether the page needs authentication
Determine how the page is intended to be accessed: public URL, session cookie, HTTP Basic Authentication, bearer token, or another authorization header. Use the screenshot provider’s documented mechanism and credentials authorized for that page. Never send a session cookie, password, or token to a provider unless you are permitted to access the page and accept the provider’s handling of those credentials.
For example, Cloudflare Browser Run documents cookies, an authenticate option for Basic Authentication, and extra request headers for token authentication. Its [screenshot endpoint guide](https://developers.cloudflare.com/browser-run/quick-actions/screenshot-endpoint/) and [API reference](https://developers.cloudflare.com/api/resources/browser_rendering/subresources/screenshot/methods/create/) describe the available fields. ScreenshotOne also documents [header and cookie methods for authenticated pages](https://screenshotone.com/docs/guides/authenticated-pages/), framed for pages you own or may access.
Session cookies
- Confirm the session is still valid and belongs to the right account and environment.
- Send only the cookies required for the target host and path, using the provider’s documented cookie format.
- Check cookie domain, path, expiration, and secure requirements. A cookie for a different host or expired session will not authenticate the capture.
- Keep secrets out of source control, logs, URLs, and shared error reports. Rotate any credential you accidentally expose.
Basic Authentication
Use the renderer’s supported Basic Authentication fields rather than guessing at URL-embedded credentials or undocumented parameters. Verify the username and password against the page in a normal browser, and check whether a reverse proxy applies the challenge to the exact URL and redirect destination being captured.
Authorization headers
Set the authorization header in the provider’s documented custom-header option. Verify the scheme (for example, Bearer), token audience and permissions, and whether redirects move the request to another host where the provider may not forward credentials. Do not place tokens in a query string unless the destination’s documented protocol requires it.
3. Separate access denial from rendering timing
Waiting can fix a page that loads content slowly, but it cannot turn a denied request into an authorized one. If the destination returns 403 before the page’s JavaScript runs, increasing a delay or waiting for a selector will not fix the permission problem.
Once the response is authorized and the page begins loading, use the renderer’s documented wait-for-selector or network-idle controls if the capture is incomplete. Cloudflare documents networkidle0, networkidle2, and waiting for a selector in its [screenshot guide](https://developers.cloudflare.com/browser-run/quick-actions/screenshot-endpoint/). These control when capture happens; they do not bypass access checks.
4. If you administer the destination site, find the blocking rule
- Open the site’s security events or equivalent request logs for the capture time. Search by the request URL, source details available to you, and any trace or request identifier.
- Identify the product and exact rule that blocked the request. Do not infer the cause from a 403 page alone.
- Confirm the request is legitimate and authorized, and that the renderer’s traffic is expected under your policy.
- Apply the narrowest correction: fix an unintended rule match, or add a scoped exception for known authorized traffic where supported.
- Retest the exact route and authentication flow, then review the resulting events and access controls.
Cloudflare’s [managed-rule troubleshooting guidance](https://developers.cloudflare.com/waf/managed-rules/troubleshooting/) recommends inspecting the event and specific match; it favors addressing a false positive at the rule level rather than disabling a whole ruleset. Available actions depend on the product and plan, so check the current account interface.
Be careful with broad allow rules. Cloudflare says an IP Access Allow rule can bypass custom rules, rate limiting, managed rules, and other checks. Where possible, use a targeted configuration, such as a supported Skip action for the relevant feature, and review exactly which protections it skips. See [IP Access rules](https://developers.cloudflare.com/waf/tools/ip-access-rules/) and [security feature interoperability](https://developers.cloudflare.com/waf/feature-interoperability/).
5. If you do not control the destination site
Use an account, session, or API credential the owner permits. Ask the site owner or screenshot provider whether the renderer is allowed and which authentication methods they support. If the site denies automated access under its policy, respect that decision. Do not attempt to evade a challenge, rotate identities to avoid a block, or disguise the renderer as a person.
6. Troubleshoot common causes
| Symptom | Cause to check | Fix |
|---|---|---|
| 403 on a page that works after signing in | The capture has no session, or the cookie is expired, scoped to another host, or missing. | Use a currently valid authorized session through the provider’s documented cookie support. |
| 403 despite sending a token | Wrong header or auth scheme, insufficient token scope, expired token, or credentials dropped after a redirect. | Compare the request with the destination’s auth contract; validate token scope and redirect behavior. |
| 403 appears as an image, not an API error | The renderer may have captured an HTML denial page as requested. | Inspect the captured page and destination response; configure the documented auth method or resolve the site-side rule. |
| Changing the user-agent has no effect | A user-agent string does not necessarily change how automated traffic is identified. | Do not rely on it to bypass bot controls. Cloudflare explicitly says its Browser Run requests are identified as bots and a configurable user-agent does not bypass bot protection. |
| Longer wait still returns 403 | The denial occurs before content rendering. | Resolve access and policy first; tune waiting only after an authorized response is returned. |
| Only one route is blocked | That route may have a distinct permission requirement or security rule. | Compare route-specific auth, redirects, and security events with a permitted route. |
| 403 began after a site security change | A new or modified rule may match the renderer request. | If you administer the site, locate the matching event and correct or narrowly scope the exception. |
| 403 comes from the screenshot endpoint itself | The API key may be missing, invalid, or unauthorized for that service. | Check the provider API credential and endpoint separately from destination-site access. |
7. Choose a screenshot service with the right controls
When selecting a renderer for pages you are authorized to access, compare its documented cookie, Basic Authentication, and custom-header support; whether it distinguishes destination errors from API errors or error-page images; its navigation and wait controls; and its credential handling. Feature availability varies by provider, so verify the current documentation rather than assuming all APIs behave alike.
Or skip the browser setup
For an authorized URL, ScreenshotNeo takes a screenshot with one GET request. It accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Its response identifies the page verdict and billing status: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. It also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));
Replace the example URL with the page you are permitted to capture. If that page requires authentication, use a documented credential option and only send credentials you are authorized to share with the service. See the ScreenshotNeo API documentation for parameters and response details. ScreenshotNeo can handle capture and cleanup; it cannot authorize access to a destination site that denies the request.
Start free with 1,000 screenshots a month and no card.
Performance, reliability, and cost notes
- Performance: Diagnose access before adding waits. Extra delay increases capture time and does not remedy a 403. Use only the navigation wait that matches the page’s behavior.
- Reliability: Record status, response details, and trace identifiers where available. Retest after changing credentials or a security rule, and verify the actual page content rather than only whether an image file was returned.
- Security: Treat cookies, passwords, and authorization headers as secrets. Limit scope and lifetime, keep them out of logs and URLs, and send them only to a provider you trust and are authorized to use.
- Cost: Repeatedly retrying a denied request is unlikely to help and may consume time or provider usage. ScreenshotNeo says failed loads, bot checks/CAPTCHAs, blank pages, timeouts, and cache hits are not billed; check response billing headers. Its listed monthly plans are Free (1,000), Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000); yearly billing gives two months free, and every feature is on every plan.
FAQ
Does a 403 mean the screenshot API is broken?
No. It means some server or security layer denied access. Identify which layer returned it before blaming the renderer.
Will a custom user-agent fix it?
Not reliably. A user-agent can be configurable without changing a service’s bot identification or the destination’s access decision.
Can waiting for network idle fix a 403?
No. Waiting helps capture content that loads late after access is granted; it does not grant access.
Should I allowlist the screenshot provider’s IP?
Only if you administer the site, the traffic is authorized, and you understand the protections the rule bypasses. Prefer the narrowest supported exception.
Can ScreenshotNeo capture any URL that returns 403?
No screenshot service can grant permission the destination has denied. Use authorized credentials or ask the site owner to permit access.


