How we reproduced an iOS Firefox only bug without an iPhone
A Frontend Debugging Trick

Oh, the glamour of frontend engineering chasing pixels across screens while battling invisible gremlins
BackStory
your app works flawlessly on desktop Chrome, kinda sorta on Android, but explodes into a caching catastrophe on iOS Safari. Sound familiar? That's exactly what bit me during a recent debugging marathon. We were simulating iOS environments, only to unearth ancient legacy logic that treated API responses like a
choose your own adventurebook based on...
Spoiler: it introduced sneaky inconsistencies that made our "responsive" design look like a bad magic trick. Buckle up, because this post dives into how user agent-based debugging saved the day, and why it's a double-edged sword
What is a user agent?
The user agent, often shortened to UA, is an HTTP request header that says what browser, engine, OS, and sometimes device is making the request. Example:
Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1
Browsers also expose this string client side as navigator.userAgent. Newer APIs expose structured data via navigator.userAgentData where supported.
Why you should treat user agent data with suspicion
It is easy to spoof.
Browser vendors change the format for privacy.
It contains lots of historical cruft, making regex detection fragile.
Relying on it for core logic creates brittle branches that are hard to test and maintain.
Simulating the Beast: DevTools to the Rescue
We went to finding solution on how we could debug this on a specific browser on a device, tried BrowserStack ,but it didn't help because we were looking for firefox on ios device
Can't afford a device farm? No problem. Modern dev tools let you override UAs like a pro, exposing those hidden branches without touching hardware.
Here's a dead simple way to spoof an iPhone UA in Chrome DevTools runnable right now:
// Open DevTools > Network > Network conditions > Custom user agent
// Paste this iPhone 14 Pro Max UA (real-world example)
const iOSUA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
// Test it in console:
console.log(navigator.userAgent.includes('iPhone')); // true after override
Step by step breakdown:
Fire up DevTools (F12), hit Network tab.
Click "More network conditions" (or Cmd+Shift+P > "Network conditions").
Uncheck "Use browser default," paste the UA, done.
We did this in a Next.js app that simulates an iOS WebView. Suddenly, API calls skipped the cache on "mobile," but fetched stale data on desktop.
How do we get this string
A buried isMobile() function sniffing UA for iOS/Android, then helping us fix it:
// DON'T DO THIS Classic legacy trash
function isMobile(ua = navigator.userAgent) {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
}
// Detect Firefox on iPhone
function isFirefoxOniPhone(ua = navigator.userAgent) {
return /iPhone/i.test(ua) && /FxiOS/i.test(ua);
}
// simple api call we want on iphone to help us get some data
function fetchData(url) {
const ua = navigator.userAgent;
// Special case: Firefox on iPhone
if (isFirefoxOniPhone(ua)) {
console.log("Firefox on iPhone detected");
return fetch(url).then(res => res.json());
}
}
console.log(fetchData(www.example.com))
Why User Agent Spoofing Helped Us Debug the Logic
In our case, the issue wasn’t really about caching or Safari itself, it was about code paths that depended on the browser environment.
Modern applications often contain conditional logic like this:
if (isMobile()) {
bypassCache();
}
The problem is that these branches only execute under specific environments. If you’re testing on desktop Chrome, that part of the code may never run.
By overriding the User Agent string, we essentially tricked the application into believing it was running on a different device, in this case an iPhone browser.
Once the browser reported itself as iOS, our application started executing the mobile-specific code path, which immediately exposed the hidden logic responsible for the inconsistent behavior.
In other words, UA spoofing allowed us to:
Reproduce a device-specific environment
Trigger code paths that normally wouldn’t run on our machine
Observe how the application behaves under those conditions
Without it, we would have needed a physical device or a remote testing environment to reproduce the issue.
The Real Lesson
User agent sniffing is often considered a fragile practice for production logic, but it can still be a powerful debugging technique.
It allows developers to simulate:
different devices
different browsers
different environments
and verify how their application responds.
In our case, the browser wasn’t the real problem; The assumptions inside our code were.





