Why Instagram Fails in Headless Chrome with Selenium and How to Fix It
Diagnose Instagram failures in Selenium headless Chrome with version checks, waits, logging, and controlled headful comparisons.
Headless Chrome is an execution mode, not proof that Instagram blocked your script. When Instagram fails under Selenium, first separate browser startup, ChromeDriver compatibility, navigation, network access, waits, and the page response. Then run the same workflow in visible Chrome and headless Chrome with every other variable held constant.
This guide shows a reproducible Selenium diagnostic workflow in Python, explains the relevant Chrome and Selenium settings, and lists fixes for common errors. The official Selenium and Chrome documentation does not establish an Instagram-specific headless bug or a verified Instagram bypass, so treat any site challenge or alternate response as an observation to document rather than a confirmed cause.
What “headless” changes
Headless Chrome runs without displaying a browser window. Chrome’s documentation says that since Chrome 112, headless and headful modes use the unified Chrome implementation; the browser still creates platform windows, but they are not shown. This means headless mode alone does not prove that a site should behave differently, although a site can return different content for many reasons.
Selenium’s Chrome documentation lists --headless=new as the current argument and says Selenium 4 is compatible by default with Chrome 75 and later. Chrome and ChromeDriver major versions should match. See the Selenium Chrome documentation and Chrome Headless documentation.
1. Capture the actual failure
Do not reduce the incident to “Instagram does not load.” Record:
- The complete exception and stack trace.
- The URL requested and the final URL reached.
- Chrome, ChromeDriver, Selenium, and Python versions.
- The exact Chrome arguments and page-load strategy.
- Whether navigation returned, timed out, or failed to start.
- The page title, a short body-text sample, and a screenshot at failure time.
- Whether the visible run produces the same page, prompt, or challenge.
A screenshot and the real exception distinguish startup errors from a missing element, an overlay, a timeout, or a site response.
2. Install Selenium and verify the browser
python -m pip install -U selenium
Recent Selenium releases include Selenium Manager, which bindings use by default to obtain a compatible driver. If Selenium cannot locate a driver, either allow Selenium Manager to resolve it or provide a valid executable path through the Chrome Service object. Selenium’s installation guidance explains both approaches: Selenium Manager.
Check the installed versions before changing code:
google-chrome --version
chromedriver --version
python -c "import selenium; print(selenium.__version__)"
Compare the major Chrome and ChromeDriver versions. A mismatch is a setup fault, not an Instagram diagnosis. For reproducible CI runs, Chrome for Testing provides matching browser and driver binaries; see Chrome for Testing.
3. Minimal headless Selenium script in Python
This script starts Chrome, opens Instagram, waits for the document body, saves a screenshot, and prints the final URL. It uses Selenium Manager by default.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
HEADLESS = True
URL = "https://www.instagram.com/"
options = Options()
if HEADLESS:
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1200")
options.add_argument("--disable-dev-shm-usage")
# Selenium Manager resolves ChromeDriver when no executable_path is supplied.
service = Service(log_output="chromedriver.log")
driver = webdriver.Chrome(service=service, options=options)
try:
driver.get(URL)
WebDriverWait(driver, 30).until(
EC.presence_of_element_located((By.TAG_NAME, "body"))
)
print("Final URL:", driver.current_url)
print("Title:", driver.title)
print(driver.find_element(By.TAG_NAME, "body").text[:500])
driver.save_screenshot("instagram-result.png")
finally:
driver.quit()
The body wait only proves that a document exists. If your next operation needs a login form, a profile link, or another specific element, wait for that element instead.
4. Use waits that match the next action
Selenium recommends explicit waits for the condition required by the next operation. Fixed sleeps and simply increasing a global timeout hide the real problem. A dynamic page can return from get() before the element your script needs is rendered.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 30)
# Examples: choose a condition that matches your workflow.
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "main")))
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "input[name='username']")))
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type='submit']")))
Selectors can change as the site changes. When an expected selector is absent, save the page source and screenshot before concluding that headless mode caused the failure.
5. Page-load strategy and navigation
Selenium’s page-load strategy controls when navigation returns:
| Strategy | Navigation returns after | Use when |
|---|---|---|
normal |
The load event and dependent resources finish | You need the usual full navigation behavior |
eager |
DOMContentLoaded fires | You will explicitly wait for application content |
none |
The initial document download starts | You have a carefully designed readiness check |
options.page_load_strategy = "eager"
A faster return requires a sufficient subsequent wait. If your code assumes that get() means the feed or login controls are ready, it can fail in both headless and visible modes.
6. Log startup and navigation failures
Enable ChromeDriver logs and preserve a screenshot at the point of failure:
from selenium.webdriver.chrome.service import Service
service = Service(log_output="chromedriver.log")
driver = webdriver.Chrome(service=service, options=options)
try:
driver.get(URL)
# workflow here
except Exception:
driver.save_screenshot("failure.png")
print("URL at failure:", driver.current_url)
print("Title at failure:", driver.title)
raise
finally:
driver.quit()
Read the first meaningful exception, not only the final timeout. A driver service failure, DNS error, TLS error, renderer crash, and missing element require different fixes.
7. Compare headful and headless runs correctly
Run the same script twice and change only the headless argument. Keep the account or session state, network and proxy, Chrome and driver versions, Selenium version, viewport, URL, actions, waits, and page-load strategy constant.
| Compare | What to record |
|---|---|
| Startup | Driver creation, Chrome logs, and exceptions |
| Navigation | Return time, final URL, redirects, and title |
| Page response | Visible prompt, login page, challenge, blank document, or expected content |
| Readiness | Which explicit wait succeeds or times out |
| Evidence | Screenshot, page source, console or driver log |
If only the site response differs, document exactly what appeared. The available evidence does not establish whether Instagram attributed that response to headless detection, account status, request rate, network reputation, or another internal rule. Do not present an evasion technique as an official fix.
8. Network, proxy, and container checks
Verify that the same machine or container can reach the target outside Selenium. Check DNS resolution, outbound firewall rules, TLS interception, proxy settings, and authentication requirements. Selenium’s documentation notes that proxy configuration can be necessary in corporate environments.
curl -I https://www.instagram.com/
When a proxy is required, configure it deliberately and use the same proxy for the controlled headful/headless comparison:
options = Options()
options.add_argument("--headless=new")
options.proxy = {
"proxyType": "manual",
"httpProxy": "proxy.example:8080",
"sslProxy": "proxy.example:8080",
}
Replace the example host with your real proxy configuration. A proxy error is not evidence of an Instagram headless block.
9. Common errors and fixes
| Error or symptom | Likely cause | Fix |
|---|---|---|
Unable to obtain driver or driver not found |
Selenium Manager cannot resolve a driver, or no executable is available | Upgrade Selenium, check network access for Selenium Manager, or pass a valid Service path. |
SessionNotCreatedException |
Chrome and ChromeDriver major versions do not match, or Chrome cannot start | Record both versions, install matching binaries, and inspect chromedriver.log. |
| Chrome exits immediately in a container | Sandbox, shared-memory, permissions, or missing dependencies | Check the container image and permissions. --disable-dev-shm-usage can help with small shared-memory mounts; do not treat it as a universal fix. |
TimeoutException waiting for an element |
Wrong selector, page not ready, redirect, prompt, or different content | Save a screenshot and URL, inspect page source, and wait for the actual next condition. |
| Blank page or renderer crash | Browser startup, resource, or environment failure | Inspect ChromeDriver logs, memory limits, dependencies, and the final URL before changing selectors. |
| Login form or challenge appears only in one mode | Different site response or session state | Repeat with controlled variables and document the exact prompt. The dossier does not verify an Instagram-specific cause. |
| Navigation hangs | Network, proxy, TLS, page-load strategy, or a page that keeps connections open | Check connectivity, choose an appropriate page-load strategy, and add an explicit readiness wait. |
10. Reproducibility and reliability checklist
- Pin or record Chrome, ChromeDriver, Selenium, and the binding version.
- Use Chrome for Testing when you need matching, reproducible binaries.
- Keep the viewport and timezone consistent between runs.
- Use a fresh temporary profile for clean diagnostics, or intentionally reuse a profile when session state is part of the test.
- Capture the final URL, title, screenshot, page source, and driver log on every failure.
- Use explicit waits tied to the next operation.
- Retry only transient startup or network failures, with a bounded retry count; do not retry a site challenge indefinitely.
- Respect Instagram’s terms, authentication requirements, and rate limits.
11. Performance and cost considerations
Headless mode can reduce desktop overhead in CI, but the dossier contains no verified Instagram or Selenium benchmark. Measure your own workflow if runtime matters. The largest delays usually come from navigation, network conditions, browser startup, rendering, and waits rather than from the visibility of the window alone.
Reuse a driver for a bounded sequence of pages when session continuity helps, then quit it to avoid leaked processes. For parallel jobs, give each driver its own profile and resources. Set timeouts that reflect the page and environment, and collect timing data around startup, navigation, readiness, and capture.
Or skip the browser setup
If your goal is a clean screenshot rather than browser automation, ScreenshotNeo provides a single GET request that returns a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners before removing more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for all options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://www.instagram.com/ -o instagram.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://www.instagram.com/"}, timeout=90)
r.raise_for_status()
open("instagram.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://www.instagram.com/' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('instagram.webp', image);
ScreenshotNeo also supports full-page capture with lazy images loaded, CSS element capture, dark mode, device presets and custom viewports, retina scale, custom CSS and JavaScript, clicks, selector waits, delays, network-idle waits, request blocking, custom headers and cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous jobs with signed webhooks, bulk capture, usage reporting, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf 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 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account.
FAQ
Does Instagram definitely block headless Chrome?
The supplied official sources do not establish that. A controlled comparison can show that responses differ, but it cannot reveal Instagram’s internal reason.
Should I add a user-agent switch to fix it?
Do not treat a user-agent change as a verified fix. First capture the exception, URL, screenshot, and logs, then compare modes with controlled variables.
Is --headless=new guaranteed to work?
It is the current Chrome argument documented by Selenium, but it does not guarantee a particular website response.
What if visible Chrome works and headless Chrome does not?
Repeat the comparison with identical versions, account or session state, network, viewport, waits, and actions. Record the exact page or prompt shown in each mode.
Can I use Selenium Manager in CI?
Yes, when the environment allows it to obtain or locate a compatible driver. For repeatable builds, pin Chrome for Testing and its matching driver.


