Regional QA · Sticky sessions

Mobile proxies for geo-testing and regional QA

Your localised build is a set of assumptions about what a user in another country sees: their currency, their tax treatment, their language, their consent banner, their payment methods, their CDN edge. Every one of those is decided by the request's apparent origin, which means the only honest way to test them is to make the request from inside the market. This is the use case where mobile proxies are least about evasion and most about simply being in the right place.

Start with $2 free creditSee pricingNo subscription · pay per GB

The problem: you cannot QA a market you cannot reach

Consumer VPNs are the usual first attempt, and they fail for a specific reason: their exit ranges are published and widely blocked, so the very geo-gating you are trying to test often refuses the VPN outright. Cloud egress fails differently — it resolves to a different CDN edge and a different geo-IP classification than any real subscriber, so you end up testing a code path no customer will ever hit. A carrier IP in the target country is simply a normal user from the application's point of view, which is exactly what a QA environment should be.

You will recognise this if

  • A currency or tax bug that only reproduces for customers, never for QA.
  • Consent banners that behave differently in production than in your test runs.
  • Geo-gated content or store variants you cannot see to sign off on.
  • A CDN edge serving stale assets in one region, invisible from head office.
  • Payment method availability that differs by country and cannot be verified pre-release.
  • Support tickets you have to close as "cannot reproduce" because you are in the wrong country.

Working code

Every block below runs as written once PROXY_URL holds a real connection string. Copy yours from the dashboard after your first top-up.

Set the connection URL

bash
export PROXY_URL="http://USERNAME-mbl-fr-sid-qa-fr-01-rot-sticky:[email protected]:7000"

# Sticky + a stable -sid- keeps the whole test session on one device,
# so the country cannot flip halfway through a checkout flow.

Playwright — assert the localisation, do not eyeball it

javascript
import { chromium, devices } from 'playwright';
import { expect } from '@playwright/test';

const url = new URL(process.env.PROXY_URL);

const browser = await chromium.launch({
  proxy: {
    server: `http://${url.host}`,
    username: decodeURIComponent(url.username),
    password: decodeURIComponent(url.password),
  },
});

// Match the locale and timezone to the exit country, or you are testing
// a combination (FR IP, en-US browser) that almost no real user has.
const context = await browser.newContext({
  ...devices['Pixel 7'],
  locale: 'fr-FR',
  timezoneId: 'Europe/Paris',
  geolocation: { latitude: 48.8566, longitude: 2.3522 },
  permissions: ['geolocation'],
});

const page = await context.newPage();
await page.goto('https://your-app.example/pricing');

await expect(page.getByTestId('price')).toContainText('€');
await expect(page.getByTestId('vat-note')).toContainText('TVA');
await expect(page.locator('#cmp-banner')).toBeVisible();   // GDPR consent

await page.screenshot({ path: 'fr-pricing.png', fullPage: true });
await browser.close();

Setting locale and timezone alongside the proxy is what makes this a realistic French user rather than a French IP with an American browser.

Run the same suite across every market

javascript
// playwright.config.js — one project per market, same specs.
const markets = [
  { name: 'fr', country: 'fr', locale: 'fr-FR', tz: 'Europe/Paris' },
  { name: 'de', country: 'de', locale: 'de-DE', tz: 'Europe/Berlin' },
  { name: 'gb', country: 'gb', locale: 'en-GB', tz: 'Europe/London' },
  { name: 'us', country: 'us', locale: 'en-US', tz: 'America/New_York' },
];

const proxyFor = (country, sid) => {
  const u = new URL(process.env.PROXY_URL);
  // Rewrite only the country and sid tokens; everything else is unchanged.
  const user = decodeURIComponent(u.username)
    .replace(/-(?:us|gb|de|fr|es|nl|pl|ch|pa|am|ge)-/, `-${country}-`)
    .replace(/-sid-[a-z0-9_]+/, `-sid-${sid}`);
  return {
    server: `http://${u.host}`,
    username: user,
    password: decodeURIComponent(u.password),
  };
};

export default {
  projects: markets.map((m) => ({
    name: m.name,
    use: {
      proxy: proxyFor(m.country, `qa-${m.name}`),
      locale: m.locale,
      timezoneId: m.tz,
    },
  })),
};

The whole market matrix is a string rewrite. No per-country infrastructure, no VPN profiles to maintain.

Which rotation mode to use

Recommended-rot-sticky

Geo-testing is the one workload on this site where you want the IP to stay exactly where it is. A test session is stateful — cookies, a consent decision, a cart, a login, a payment flow — and an IP that rotates mid-run will either log you out or, worse, quietly re-evaluate your country between steps and give you a result that does not correspond to any real user journey. Pin the session with -rot-sticky and a stable -sid-, one per concurrent test worker, and the exit stays put for the duration.

When to choose something else

ModeUse it when
hardLong-running suites where you want the strictest device pin the pool offers.
auto10Only for stateless spot checks — "does this URL redirect correctly from Poland" — where nothing carries between requests.
noneNot recommended for QA. Leaving rotation to the gateway default makes a failing test hard to attribute.

Expected bandwidth use

QA runs render real pages in a real browser, so budget like a browser. The good news is that test suites are usually small in absolute terms — you are running tens or hundreds of journeys, not millions of fetches.

WorkloadPer unitPer GB
Single page load, full assets, cold cache2–5 MB~200–520 loads
Complete checkout journey (5–8 steps)6–15 MB~70–170 journeys
Page load with images/fonts/media blocked300–700 KB~1,500–3,500 loads
Redirect/header spot check (no render)~5 KB~200,000 checks

Reuse one browser context across the assertions in a journey instead of launching per test — a warm cache removes most of the repeat asset cost. And run the full-asset suite on a schedule while your per-commit CI run uses the asset-blocked variant; most localisation assertions are about text and headers, which do not need the images. Ranges above assume typical commercial web apps; meter your own suite before sizing.

BandwidthPricePer GBSave
1 GB$7$7—
5 GB$35$7—
10 GB$68$6.803%
25 GB$150$614%
50 GB$283$5.6619%
100 GB$500$529%

Relevant countries

Change country by editing one token in the proxy username — no redeploy, no second account. Pool depth varies by country and by whether you use the mobile (mbl) or residential (peer) pool; live availability is shown on each country page and in your dashboard.

Legitimate use only

ProxyMobile is sold for measurement, quality assurance and research on systems you own or are authorised to test. Buying bandwidth does not grant permission to access a third party's systems. You remain responsible for complying with each target's terms of service, robots directives and rate limits, and with the data-protection law that applies to you.

For this use case

  • Test applications you own, or that a client has authorised you in writing to test.
  • Geo-testing is for verifying your own regional behaviour — not for consuming a third party's geo-licensed media outside its licensed territory.
  • Keep synthetic test traffic out of your production analytics, and do not place real orders you will not honour.
  • If your suite touches a payment provider, use their sandbox. Driving live payment rails with test traffic is a compliance problem regardless of the proxy.

Never, on any plan

  • No credential stuffing, account takeover, or any attempt to authenticate as someone else.
  • No circumventing an access control, paywall or security measure you have not been authorised to test.
  • No collection of personal data you have no lawful basis to process.
  • No load that a reasonable operator would experience as a denial of service — throttle, back off on 429s, and honour Retry-After.

Accounts used for fraud, intrusion or bulk abuse are terminated without refund, and we cooperate with valid legal process. Full terms are in our acceptable use policy.

Geo testing & QA FAQ

Why not use a VPN for geo-testing?

Consumer VPN exit ranges are published and widely blocked, so the geo-gating you are trying to test frequently refuses the VPN outright — you learn that the site blocks VPNs, not whether your localisation works. A carrier IP looks like an ordinary subscriber, so the application takes the normal code path.

Which rotation mode for a test suite?

Sticky, with a stable -sid- per worker. Test journeys are stateful; an IP that changes mid-journey can log the session out or re-evaluate the country between steps, producing failures that have nothing to do with your build.

Can I run this from CI?

Yes. It is one proxy setting in your Playwright, Puppeteer or Selenium config, so it works the same in CI as locally. Give each parallel worker its own -sid- so they do not share a device.

Do I need to set locale and timezone too?

You should. An FR exit IP with an en-US browser and a New York timezone is a combination almost no real user has, and it will exercise a different code path than the customer you are trying to emulate. Set all three together.

How much bandwidth does a QA suite need?

A full checkout journey with all assets runs roughly 6–15 MB, so about 70–170 journeys per gigabyte. Blocking images and fonts gets you several times more, and most localisation assertions are about text and headers anyway.

Related guides

Try it against your own targets

Bandwidth estimates on this page are honest ranges, not promises — the only number that matters is what your targets cost you. Your first top-up is credited with an extra $2, which is enough to meter a real run before you size a budget.