Using Singapore mobile proxies with puppeteer-extra-stealth in 2026
Using Singapore mobile proxies with puppeteer-extra-stealth in 2026
You have puppeteer-extra-stealth running, the plugin stack is tuned, navigator.webdriver is masked, and the browser fingerprint looks clean. Then you point it at a Shopee listing, a Grab merchant page, or a SingPass-gated flow, and you hit a CAPTCHA wall or a silent block within three requests. The stealth plugins are doing their job. The IP is the problem. In 2026, the threat models that matter for Southeast Asian targets have shifted almost entirely toward IP reputation scoring and carrier-level ASN checks. That is what this post is about.
why puppeteer-extra-stealth hits walls without residential mobile IPs
puppeteer-extra-stealth is excellent at what it does: patching Chrome’s automation surface so the browser looks like a human-operated instance at the JavaScript layer. But platform anti-bot systems in 2026 operate in layers, and the JS fingerprint layer is only one of them. The layer underneath, IP reputation scoring, runs before your JavaScript ever executes. If your IP resolves to a Hetzner, DigitalOcean, or AWS ASN, the decision to serve a CAPTCHA or a fake-success response is made at the edge, not by any in-page script.
For Southeast Asian platforms specifically, this problem is sharper than it is in US or EU markets. Shopee, Lazada, and several Singapore government services have built allowlists that prefer or trust traffic originating from SingTel, StarHub, and M1 ASNs. This is not speculation; you can confirm it by checking the cf-ipcountry and x-real-ip headers on logged requests and comparing the ASN of your requests against what real Singapore mobile users send. When you use a US residential pool, you get a US ASN even if the exit country is labeled Singapore. When you use a datacenter IP in Singapore, the ASN resolves to a hosting provider, not a telco. Neither passes the carrier-level check.
There is also a secondary problem specific to puppeteer-extra-stealth workflows: session duration. Many stealth operators run long-lived browser contexts to accumulate cookie trust before doing any meaningful action. Long-lived datacenter sessions are a strong signal to bot detection systems because real users on mobile carriers get new IPs relatively frequently, either through DHCP churn or manual reconnections. A single datacenter IP holding an authenticated session for four hours is anomalous. A SingTel IP holding a session for twenty minutes, then a new StarHub IP picking up a fresh session, looks like normal consumer behavior. The HTTP vs SOCKS5 mobile proxies post covers the protocol tradeoffs in more depth if you are deciding which endpoint type to use for session continuity.
setting up SMP credentials in puppeteer-extra-stealth
Singapore Mobile Proxy provides credentials in the standard ip:port:username:password format. Both HTTP and SOCKS5 endpoints are available; for puppeteer-extra-stealth, SOCKS5 is preferable if you are also proxying WebSocket traffic or running Chrome with --proxy-server as a launch flag, because SOCKS5 tunnels all TCP traffic uniformly. HTTP proxy mode is fine for most HTTPS scraping tasks.
Here is a working integration using puppeteer-extra, puppeteer-extra-plugin-stealth, and the proxy-chain package for authenticated proxy support (Chrome does not natively handle username:password in --proxy-server for SOCKS5):
import puppeteer from 'puppeteer-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import proxyChain from 'proxy-chain';
puppeteer.use(StealthPlugin());
// SMP credential format: ip:port:username:password
const SMP_PROXY = '103.x.x.x:10000:smp_user:smp_pass';
async function launchWithSMPProxy(smpCredential) {
const [host, port, username, password] = smpCredential.split(':');
const upstreamProxy = `http://${username}:${password}@${host}:${port}`;
// proxy-chain creates a local unauthenticated proxy that forwards
// to the authenticated upstream, which Chrome accepts via --proxy-server
const localProxy = await proxyChain.anonymizeProxy(upstreamProxy);
const browser = await puppeteer.launch({
headless: true,
args: [
`--proxy-server=${localProxy}`,
'--no-sandbox',
'--disable-setuid-sandbox',
],
});
browser.on('disconnected', () => {
proxyChain.closeAnonymizedProxy(localProxy, true);
});
return browser;
}
async function run() {
const browser = await launchWithSMPProxy(SMP_PROXY);
const page = await browser.newPage();
// verify the IP resolves to a SG carrier ASN before doing real work
await page.goto('https://api.ipify.org?format=json');
const body = await page.evaluate(() => document.body.innerText);
console.log('exit IP:', JSON.parse(body).ip);
await page.goto('https://shopee.sg', { waitUntil: 'networkidle2' });
// ... your automation logic
await browser.close();
}
run();
A few notes on this setup. The proxy-chain package is doing important work here: Chrome’s --proxy-server flag does not support inline credentials for SOCKS5 or even for HTTP proxies in newer Chromium builds. Trying to pass --proxy-server=http://user:pass@host:port directly will silently fail on auth. Using proxyChain.anonymizeProxy wraps the upstream in a local proxy on a random port that Chrome can connect to without credentials. This is the correct pattern and the one that actually works in 2026 Chromium versions.
If you are on an older Node stack and prefer http-proxy-agent or the socks package for page-level request interception instead of a browser-level proxy, those approaches work too but require you to intercept every request with page.setRequestInterception(true), which has performance overhead and breaks service workers.
rotating IPs per request or per session
SMP offers two endpoint modes: sticky sessions, where the same exit IP is held for a configurable duration, and rotating endpoints, where each new connection gets a fresh carrier IP. Choosing between them is not a technical question; it is a workflow question.
Use sticky sessions when your automation requires a browser context to build trust over time, such as logging into an account, accumulating a browsing history before triggering a conversion event, or maintaining a Shopee or Lazada session across multiple page loads. Cookie jars are tied to the exit IP in the eyes of many session integrity systems; if the IP changes mid-session, the platform may invalidate the session or serve a re-authentication challenge.
Use rotating endpoints when each request is stateless or when you are doing high-volume data collection where per-request IP freshness matters more than session continuity. SERP scraping, price monitoring, and public listing crawls all fit this pattern.
// rotating: get a fresh carrier IP on each browser launch
// by calling launch() again rather than reusing the browser instance
async function collectWithRotation(urls, smpCredential) {
const results = [];
for (const url of urls) {
const browser = await launchWithSMPProxy(smpCredential);
const page = await browser.newPage();
try {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
const data = await page.evaluate(() => document.title); // replace with real extraction
results.push({ url, data });
} catch (err) {
results.push({ url, error: err.message });
} finally {
await browser.close();
}
}
return results;
}
Launching a new browser per URL is heavier than rotating via a connection pool, but for puppeteer-extra-stealth workflows it is the cleanest approach because it also resets the browser fingerprint state between requests. A fresh browser context means fresh canvas noise, fresh WebGL renderer string randomization (if you have those plugins loaded), and no cross-session fingerprint leakage.
For sticky sessions, SMP exposes a session token mechanism through the credentials themselves. The product documentation on the plans page at /) covers the exact parameter format for requesting a sticky lease. The rotation behavior you get from simply re-dialing the same endpoint versus passing a consistent session identifier is different, and reading those docs before building your rotation logic will save you some surprises.
three real workflows where this combo wins
monitoring Singapore e-commerce listings at scale
Shopee SG and Lazada SG both operate aggressive bot filters on their listing and search endpoints. In mid-2025 both platforms added carrier-level checks that silently return stale cached data or stripped responses to non-residential IPs. This is not a block you can detect from a 4xx status code; you get a 200 with content that is missing the real-time stock and pricing fields.
With SMP’s SingTel and StarHub IPs, requests look like organic mobile app traffic. The stealth plugin keeps your browser fingerprint from contradicting the mobile carrier origin. Combined, the responses you get match what a real Singapore consumer sees on their phone. If you are doing price intelligence or inventory monitoring for SEA markets, this is the only setup that gives you ground truth.
testing SingPass and government service flows
SingPass and related government digital service portals are geofenced at the IP layer for certain flows, particularly anything related to Myinfo data. Testing these integrations requires IPs that resolve to Singapore carriers, not Singapore datacenters. SMP’s real residential modem endpoints clear this check. Developers building Myinfo-integrated applications in staging can use SMP to simulate the actual network origin of a Singapore resident without needing a VPN that might trigger other security rules.
SEO monitoring for Singapore SERPs
Google serves different organic results, different local pack results, and different shopping ads based on the IP’s carrier and country. US residential pools labeled “Singapore” still resolve to non-SG ASNs at the carrier level and often return hybrid results influenced by US infrastructure. For accurate Singapore SERP tracking, whether you are monitoring positions for a client or doing keyword research, the ASN needs to match a SG telco. This is a workflow the mobile proxies for SEO research post covers in more depth, but the puppeteer-extra-stealth angle is that you need both the stealth fingerprint and the correct carrier IP to avoid Google’s automated query detection and to get results that are actually representative of what Singapore users see.
common pitfalls
-
user-agent and IP type mismatch. puppeteer-extra-stealth defaults to a desktop Chrome user-agent. If you are routing through a mobile carrier IP, some platforms will flag the mismatch between a desktop UA and a mobile ASN. Either set a realistic Android Chrome user-agent (
page.setUserAgent(...)) or make sure your stealth configuration is handling UA consistency. The mobile carrier IP is a strong signal, and your UA should not contradict it. -
IP rotation mid-session breaking cookie state. Rotating the proxy between page loads inside a single browser context does not work cleanly. The cookie jar persists in the browser process but the session tokens issued by the platform are often bound to the originating IP. If you need rotation, rotate at the browser level (close and relaunch), not at the proxy connection level.
-
not verifying the exit IP before starting work. Always do a quick IP check request at the start of a browser session to confirm you are actually exiting through the SMP endpoint and not falling back to your host machine’s IP due to a proxy configuration error. Silent proxy failures are common and expensive to debug after the fact.
-
ignoring SOCKS5 vs HTTP for WebSocket-heavy targets. Some platforms, including certain Shopee and Grab features, open WebSocket connections for real-time pricing or chat. HTTP proxy mode does not tunnel WebSockets correctly in all configurations. Use SOCKS5 for targets you know use WebSockets.
-
over-rotating sticky sessions. Setting a sticky session duration that is too short (under five minutes) and hammering the same platform with many sessions creates a pattern that looks like a distributed bot. Give sticky sessions enough time to behave like a real user session, and spread request volume across the available session pool.
-
not reading the terms of the platforms you are targeting. Mobile proxies make your automation technically harder to detect, but they do not change the legal or contractual picture. The ethical mobile proxy use post is worth reading if you are operating in a commercial context where ToS compliance matters to your business.
when Singapore IPs specifically matter
A lot of proxy providers offer “Singapore” endpoints that are actually routed through Singapore-based datacenter infrastructure operated by AWS, GCP, or regional colocation facilities. For many use cases that does not matter. For SEA-facing platforms in 2026, it matters a great deal. SingTel, StarHub, M1, and Vivifi ASNs are on implicit allowlists that datacenter ASNs are not. More practically, platforms serving Singapore consumers have calibrated their trust signals against the actual distribution of traffic they see from real users, and the majority of that traffic comes from mobile carriers, not datacenters.
There is also a latency and geolocation consistency angle. When your IP resolves to a SG carrier ASN, the geolocation APIs that many platforms call internally return coordinates consistent with Singapore residential areas. When you use a datacenter IP, the geolocation response might say Singapore but the precision is low, and the IP type classification (residential vs. hosting) is often surfaced directly to the platform’s fraud stack. SMP’s modem-backed IPs resolve to residential mobile classifications in the IP intelligence databases that Cloudflare, Akamai, and most custom fraud systems query. That classification difference is what changes the outcome for the workflows described above.
getting started
If you want to run this locally before committing to a plan, the workflow is straightforward: grab credentials from the Singapore Mobile Proxy plans page, drop the ip:port:username:password string into the launchWithSMPProxy call above, and point it at api.ipify.org to verify the exit IP. From there, the integration works the same as any other proxy-backed puppeteer setup, with the difference that the IP quality is doing work that no amount of stealth plugin tuning can replicate. For operators already running puppeteer-extra-stealth against SEA targets who are hitting invisible blocks, switching to SMP credentials is usually the change that moves the needle.