How to Test Deep Links on Web (Complete Guide)
Deep links enable users to jump straight into a specific piece of content or functionality from an external source such as an email, SMS, QR code, or another website. When a deep link works, the user
Why Deep Links Matter on the Web
Deep links enable users to jump straight into a specific piece of content or functionality from an external source such as an email, SMS, QR code, or another website. When a deep link works, the user experience feels seamless: a marketing campaign drives traffic straight to a product page, a support ticket includes a link that opens the exact conversation thread, or a social share returns the reader to the article they were reading. If the link fails, the user lands on a generic homepage, sees an error page, or is forced to re‑authenticate, which instantly erodes trust and can increase bounce rates by double‑digit percentages. In production, broken deep links also undermine attribution analytics, making it impossible to measure the true ROI of campaigns. Therefore, testing deep links is not a nicety; it is a core part of guaranteeing that the web application behaves as a predictable entry point for any external referrer.
What Breaks in Production
Production environments expose deep‑link failures that unit tests rarely catch. Common failure modes include:
- Misconfigured server redirects – a 302 that points to the wrong route after authentication.
- Service worker interference – caching stale manifests that cause the link to open an offline fallback instead of the intended page.
- Fragment loss – browsers stripping the hash during a redirect chain, breaking anchor‑based navigation.
- Third‑party cookie blocking – links that rely on session cookies being sent across domains fail when SameSite attributes are too strict.
- Ad‑blocker or privacy‑extension filtering – URLs containing known tracking parameters are blocked, preventing the deep‑link logic from running.
- Dynamic import timing – code‑splitting that defers the route‑resolution module until after the URL has already been processed, resulting in a blank screen.
- Server‑side rendering mismatches – the SSR HTML renders a different route than the client‑side hydration expects, causing a hydration error and a white screen.
Each of these issues surfaces only when the link is exercised from an external context with real network conditions, authentic cookies, and a real browser profile.
Deep Link Fundamentals for Web Applications
Understanding the mechanics helps you design tests that cover the right layers.
URI Schemes vs. HTTPS Links
Traditional mobile apps use custom schemes (e.g., myapp://product/123). On the web, the standard is an HTTPS URL that the server can interpret. Some progressive web apps (PWAs) also register URL handlers via the manifest.json "scope" and "start_url" fields, enabling the OS to launch the PWA when a matching link is tapped. For pure web sites, the deep link is simply an HTTPS path that the router must map to a component.
Server‑Side Redirects and Status Codes
When a link arrives at the domain, the server may respond with:
- 200 OK – the HTML (or JSON) for the target route is returned directly.
- 301/302 – a redirect to another URL, often after authentication or locale negotiation.
- 404 – the path does not exist; the server may serve a custom 404 page or fallback to the homepage.
- 401/403 – authentication required; the server redirects to a login page, preserving the original URL in a query parameter (commonly
redirect_uriorstate).
Your tests must verify that the final rendered page matches the expected state after any redirect chain.
Client‑Side Routing
Single‑page applications (SPAs) intercept navigation events via the History API (pushState, replaceState) or the hashchange event. The router reads window.location.pathname (and optionally window.location.hash) to decide which component to mount. If the JavaScript bundle fails to load or the router throws an exception, the user sees a blank screen or an error boundary fallback.
Service Workers and Caching
A service worker can intercept fetch events and serve a cached response. If the cached response is the shell (index.html) but the runtime fails to fetch the route‑specific chunk, the deep link will render the shell without content. Testing must therefore include a “cache‑bypass” mode (e.g., disabling the service worker or using network-first strategy) to confirm that the network path works.
Authentication Persistence
Deep links that target protected resources rely on the browser sending cookies or token storage (localStorage, sessionStorage, IndexedDB) with the request. If the authentication mechanism is tied to a subdomain or a different port, cross‑origin requests may be blocked by SameSite cookie settings, causing the server to treat the request as unauthenticated and redirect to login.
Test Matrix for Deep Links
Below is a comprehensive matrix that you can use as a checklist when planning manual or automated tests. Each row represents a test scenario; columns indicate the dimension being validated.
| Scenario ID | Description | Expected Result | Pass/Fail Criteria | Notes |
|---|---|---|---|---|
| DL‑01 | Happy‑path HTTPS deep link to a public product page (no auth) | Page renders product details, URL matches link | Visual verification + DOM check for product ID | Baseline |
| DL‑02 | Happy‑path deep link to a protected page after login | User is redirected to login, after successful auth lands on target page | Verify redirect chain (login → target) and final URL | Test with fresh incognito profile |
| DL‑03 | Deep link with query parameters used for tracking (utm_source, utm_medium) | Parameters preserved in final URL (or stripped per policy) | Compare initial and final URL | Important for attribution |
| DL‑04 | Deep link that includes a fragment identifier (#section-2) | Fragment retained after any redirects, page scrolls to element | Check window.location.hash and scroll position | Tests hash‑preservation |
| DL‑05 | Deep link accessed while service worker is active (cache‑first) | Same result as DL‑01 (network‑first fallback if needed) | Disable SW or use devtools to bypass cache | Ensures SW does not break navigation |
| DL‑06 | Deep link from a cross‑origin email client (different domain) with SameSite=Lax cookie | Login redirect preserves original URL in state parameter | Verify state param and final destination | Checks cross‑origin auth flow |
| DL‑07 | Deep link with malformed UTF‑8 characters in path | Server returns 400 Bad Request or 404, not 500 | Status code and error message | Input validation |
| DL‑08 | Deep link that exceeds browser URL length limit (~2KB) | Server returns 414 URI Too Long or truncates gracefully | Status code or fallback handling | Edge case for SEO‑heavy links |
| DL‑09 | Deep link after a locale‑based redirect (e.g., /en/product → /fr/produit) | Final page shows correct locale and content | Verify lang attribute and localized strings | Tests i18n routing |
| DL‑10 | Deep link that triggers a modal or dialog on mount (e.g., promo banner) | Modal appears, focus trapped, ESC closes it | ARIA attributes and focus order | UX + accessibility |
| DL‑11 | Deep link accessed with a slow 3G network (simulated) | Page loads within acceptable time (<5 s) and shows loading spinner then content | Performance metrics (FCP, LCP) | Real‑world condition |
| DL‑12 | Deep link opened in a private/incognito window with no existing session | Redirect to login, after auth lands on target | Same as DL‑02 but with cleared storage | Confirms no stale session leakage |
| DL‑13 | Deep link that points to a deleted resource (ID no longer in DB) | Server returns 410 Gone or 404 with helpful message | Status code and message content | Tests stale link handling |
| DL‑14 | Deep link that includes a potentially dangerous script (javascript:alert(1)) in query param | Script is not executed; URL is either rejected or sanitized | No alert, CSP violation reported if applicable | Security test |
| DL‑15 | Deep link accessed via a screen reader (VoiceOver, NVDA) | All dynamic content announced, focus managed correctly | ARIA live regions, announcement order | Accessibility validation |
| DL‑16 | Deep link triggered from a QR code scanned with a low‑resolution camera | Same as DL‑01 (no distortion) | Visual verification | Confirms encoding robustness |
| DL‑17 | Deep link that initiates a file download (e.g., /report/pdf/123) | File downloads with correct name and MIME type | Download event, file size, extension | Tests non‑navigation deep links |
| DL‑18 | Deep link that attempts to navigate to a external domain (https://evil.com) | Browser blocks navigation or shows interstitial warning | Navigation prevented or user prompted | Checks open‑redirect protection |
| DL‑19 | Deep link after a server‑side redirect loop (misconfig) | Browser shows “too many redirects” error after ~20 hops | Detect loop and abort | Prevents infinite redirect |
| DL‑20 | Deep link that triggers a feature flag‑gated UI (beta feature) | UI appears only when flag is enabled for the user | Feature flag state + UI presence | Tests configuration‑dependent paths |
You can extend this matrix with additional rows for performance budgets, specific error‑code handling, or proprietary analytics events.
Manual Testing Approach
A disciplined manual process complements automation by catching subtle UX and environmental issues that scripts may overlook.
Setup
- Create a clean browser profile – use Firefox’s “Profile Manager” or Chrome’s
--user-data-dirflag to start with no extensions, no cache, and no stored cookies. - Install essential debugging tools – DevTools Network throttling, Service Worker pane, and the “Redirects” tab in the Network console.
- Prepare a list of deep‑link URLs – export from your CMS, marketing spreadsheet, or routing configuration. Include variations with query strings, fragments, and encoded characters.
- Define test personas – curious (explores every link), impatient (quick taps, expects instant load), novice (may miss error messages), adversarial (tries malformed URLs), and accessibility (uses screen reader, keyboard only).
Step‑by‑Step Execution
For each URL in the list, follow this routine:
- Copy the link to the clipboard.
- Open a new incognito window (ensures no session bleed).
- Paste and navigate – press Enter. Observe the address bar for any immediate redirects.
- Open DevTools → Network and enable “Preserve log”.
- Watch the request chain – note each status code, redirect URL, and any blocked requests (e.g., by CSP).
- After the final response, switch to the Elements tab and verify:
- The expected container element exists (e.g., ).
- Any dynamic content (loaded via lazy‑load or fetch) appears within the performance budget.
- If a modal should open, confirm that focus is trapped and that the modal has
role="dialog"andaria-modal="true".- Check accessibility – run the axe core extension or manually tab through the page, ensuring that all interactive elements are reachable and that live regions announce updates.
- Test error paths – deliberately modify the URL (e.g., change an ID to a non‑existent value, add a stray
&) and repeat steps 2‑7. Verify that the server returns the appropriate error code and that the UI shows a helpful message (not a raw stack trace). - Repeat with network throttling – set DevTools to “Slow 3G” or “Offline” and observe fallback behavior (e.g., service worker serving an offline page).
- Document findings – capture screenshots, HAR files, and console errors in a shared test‑run spreadsheet.
Tools that Assist Manual Testing
- Redirect Path (Chrome extension) – shows each hop in a redirect chain instantly.
- URL Decoder/Encoder – helps you quickly craft malformed URLs for adversarial tests.
- Lighthouse – run with the “SEO” and “Accessibility” categories to get automated hints on missing meta tags or ARIA issues.
- curl -v – useful for checking server‑side responses without a browser (e.g., verifying that a 302 includes the correct
Locationheader).
When you have completed the matrix for all URLs, you will have a high‑confidence baseline that can be fed into automated test generation.
Automated Testing Approaches
Automation scales the matrix across browsers, devices, and CI pipelines. Below are patterns that work well for web deep‑link validation.
Unit‑Level Route Guard Tests
If your framework exposes a router (e.g., React Router, Vue Router, Angular Router), write unit tests that feed a location object to the router’s resolver and assert the resulting component or redirect. Example with React Router v6 using Jest:
import { createMemoryRouter, RouterProvider } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; import App from './App'; test('public product deep link renders product details', () => { const router = createMemoryRouter([ { path: '/product/:id', element: <|reserved_token_163654|>``` We need to continue the article. We have started a code snippet but it got cut off. Let's continue writing the article fully, ensuring we reach ~4000 words. We'll need to write many more paragraphs. Let's proceed. We need to be careful not to use prohibited words. Also avoid filler. We'll continue with the automated testing section, then move on to edge cases, accessibility/security, checklist, takeaways. We'll mention SUSA in at most two sections; we can mention it in the Automated Testing Approaches section (maybe as an example of autonomous exploration) and maybe also in the Checklist section (briefly). Ensure not more than two mentions. We need to produce a lot of text. Let's aim for about 35-40 paragraphs of ~110-130 words each = ~4300-5200 words. We'll try to stay within 3800-4500. Let's target about 30 paragraphs of ~130 words = 3900. We'll write detailed paragraphs. We have already written some sections: Introduction, What Breaks, Fundamentals, Test Matrix (with table), Manual Testing Approach. Now we need to continue. We'll write the Automated Testing Approaches section with multiple H3 subsections: Unit-level route tests, Integration tests with a test server, End-to-end tests with Playwright/Cypress, Using SUSA for autonomous exploration, Visual regression for deep-link landing pages, Performance testing with Lighthouse CI, API contract testing for redirect endpoints. We'll include code snippets for Playwright, Cypress, maybe a sample npm script. Then Edge Cases Only Visible in Production: network conditions, browser extensions, private browsing, incognito, service worker updates, iOS/Android web view differences, Safari ITP, Chrome SameSite changes, ad-blocker filtering, VPN/proxy, DNS rebinding, etc. Then Accessibility and Security Considerations: WCAG checks, focus management, ARIA, CSP, referrer policy, cookie SameSite, CSRF tokens, rate limiting on redirect endpoints, open redirect protection, etc. Then Checklist for Deep Link Testing: a concise bullet list. Then Takeaways and Closing. Now we need to write the rest, ensuring we hit the word count. Let ## Automated Testing Approaches Automation turns the matrix from a manual checklist into a repeatable gate that runs on every commit. The key is to test at the right layer: unit tests for routing logic, integration tests for server‑side redirects, and end‑to‑end (E2E) tests for the full browser experience. Each layer can be enriched with tooling that catches specific classes of bugs. ### Unit‑Level Route Guard Tests When your SPA exposes a router object, you can invoke it directly with a location string and assert the resolved route or redirect. This is fast, deterministic, and ideal for catching regressions in route definitions or guard functions. **React Router v6 example (Jest + @testing-library/react):**import { createMemoryRouter, RouterProvider } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import App from './App';
test('public product link resolves to ProductDetails component', () => {
const router = createMemoryRouter([
{ path: '/product/:id', element:
}, { path: '/login', element:
}, { path: '*', element:
}, ]);
render(
); // Simulate navigation via URL
window.history.pushState({}, '', '/product/42');
window.dispatchEvent(new PopStateEvent('popstate'));
// Wait for the component to mount
expect(screen.getByRole('heading', { name: /product 42/i })).toBeInTheDocument();
});
test('protected link redirects to login preserving state', () => {
const router = createMemoryRouter([
{ path: '/protected', element:
}, { path: '/login', element:
}, ]);
render(
); window.history.pushState({}, '', '/protected?ref=email');
window.dispatchEvent(new PopStateEvent('popstate'));
// Assuming your auth guard redirects to /login?state=...
expect(screen.getByLabelText(/username/i)).toBeInTheDocument();
expect(window.location.search).toContain('state=');
});
The same pattern works for Vue Router (`router.push`) and Angular Router (`TestBed.runInInjectionContext`). Keep these tests in a `__tests__/routing` folder; they run in milliseconds and give instant feedback when a route path is renamed or a guard logic changes. ### Integration Tests with a Test Server Unit tests cannot verify that the server actually returns the expected redirect or that cookies are forwarded correctly. Spin up a lightweight instance of your API (or use a tool like **msw** – Mock Service Worker) and exercise the network layer. **Example with MSW and Playwright:**// msw handlers for deep-link endpoints
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const handlers = [
rest.get('/product/:id', (req, res, ctx) => {
const { id } = req.params;
if (id === '999') {
return res(ctx.status(410), ctx.json({ error: 'gone' }));
}
return res(
ctx.status(200),
ctx.json({ id, name:
Product ${id}}));
}),
rest.get('/login', (req, res, ctx) => {
// preserve original URL in state param
const redirect = req.url.searchParams.get('state') || '/';
return res(ctx.redirect(
${redirect}?loggedIn=1));}),
];
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
In your Playwright test, you then navigate to the mocked domain and assert the final state:test('deep link to missing product shows gone message', async ({ page }) => {
await page.goto('http://localhost:1234/product/999');
await expect(page.locator('text=gone')).toBeVisible();
await expect(page).toHaveURL(/.*\/product\/999/);
});
Because MSW intercepts at the network level, you can simulate latency, error codes, or header modifications without touching real infrastructure. This approach catches bugs where a redirect mistakenly drops query parameters or where a 410 is mishandled as a 500. ### End‑to‑End Tests with Playwright / Cypress E2E tests launch a real browser, load your application, and follow the exact steps a user would take. They are slower but provide the highest confidence for deep‑link flows that involve authentication, service workers, or third‑party scripts. **Playwright script for a protected deep link:**const { test, expect } = require('@playwright/test');
test('protected deep link redirects to login then to target after auth', async ({ page }) => {
// 1. Navigate directly to the protected URL (no session)
await page.goto('https://app.example.com/dashboard/reports/2024');
// 2. Expect redirect to login page with state preserving original URL
await expect(page).toHaveURL(/login\?state=/);
await expect(page.locator('input[name="email"]')).toBeVisible();
// 3. Fill credentials (using a test user)
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'SafePass!123');
await page.click('button[type="submit"]');
// 4. After successful login, the app should redirect back to the original URL
await expect(page).toHaveURL(/**/, url => {
return url.pathname === '/dashboard/reports/2024' && url.searchParams.get('loggedIn') === '1';
});
// 5. Verify that the report container is rendered
await expect(page.locator('[data-report-id="2024"]')).toBeVisible();
});
**Cypress equivalent:**describe('Deep link authentication flow', () => {
it('redirects to login and returns to target after auth', () => {
cy.visit('https://app.example.com/dashboard/reports/2024');
cy.url().should('include', '/login');
cy.get('input[name="email"]').type('test@example.com');
cy.get('input[name="password"]').type('SafePass!123{enter}');
cy.url().should('eq', 'https://app.example.com/dashboard/reports/2024?loggedIn=1');
cy.get('[data-report-id="2024"]').should('be.visible');
});
});
Both frameworks allow you to emulate network conditions (`page.setOffline(true)` in Playwright, `cy.intercept` with `delay` in Cypress) and to clear storage between runs (`page.context().clearCookies()` or `cy.clearCookies()`). Include these steps in a `beforeEach` hook to guarantee a clean state for each deep‑link scenario. ### Using SUSA for Autonomous, Persona‑Driven Exploration SUSA (the autonomous QA platform) can be pointed at your staging URL or fed an APK wrapper that loads your web view. Without writing any scenario, SUSA will: - Crawl the site using a variety of user personas (curious, impatient, novice, adversarial, elderly, accessibility‑focused, power user). - Follow every discovered link, including those buried in JavaScript‑generated menus, and treat each as a potential deep‑link entry point. - Detect crashes, ANRs (ifrs (if wrapped in a WebView), dead buttons, WCAG violations, and security issues such as open redirects or missing CSP headers. - Auto‑generate regression scripts in Playwright (Android) or Playwright (Web) that capture the exact navigation sequences it exercised, giving you a starting point for deterministic tests. To invoke SUSA from the command line after installing the agent:pip install susatest-agent
susatest run --url https://staging.example.com --personas all --output ./susareport
The resulting report lists each deep link that was attempted, the outcome (PASS/FAIL), and any anomalies (e.g., a link that triggered a modal that stole focus). Because SUSA explores without preconceived scripts, it often finds links that your test matrix omitted—such as a deep link generated by a third‑party widget or a URL constructed dynamically from user‑generated content. ### Visual Regression for Deep‑Link Landing Pages Even when the DOM is correct, subtle styling shifts can make a page look broken. Tools like **Chromatic** (for Storybook) or **Percy** (integrated with Playwright) capture screenshots of the rendered page after a deep‑link navigation and compare them against a baseline. **Percy + Playwright example:**import { test, expect } from '@playwright/test';
import { percySnapshot } from '@percy/playwright';
test('visual check for product deep link', async ({ page }) => {
await page.goto('https://app.example.com/product/az-123');
await percySnapshot(page, 'Product page – default theme');
// optionally test dark mode
await page.evaluate(() => document.documentElement.classList.add('dark'));
await percySnapshot(page, 'Product page – dark theme');
});
If a new CSS rule accidentally hides the “Add to cart” button, the snapshot diff will flag it before the change reaches production. ### Performance Testing with Lighthouse CI Deep links that land on a heavy page can degrade perceived performance, especially on slower connections. Lighthouse CI can be run as part of your CI pipeline to enforce budgets on First Contentful Paint (FCP), Largest Contentful Paint (LCP), and Time to Interactive (TTI). **lighthouserc.json:**{
"ci": {
"collect": {
"url": [
"https://app.example.com/product/best-seller",
"https://app.example.com/checkout"
],
"settings": {
"preset": "desktop",
"formFactor": "desktop",
"screenEmulation": { "mobile": false, "width": 1366, "height": 768 },
"throttling": { "rttMs": 150, "throughputKbps": 1.5 * 1024, "cpuSlowdownMultiplier": 2 }
}
},
"assert": {
"preset": "lighthouse:recommended",
"assertions": {
"categories:performance": ["warn", { "minScore": 0.9 }],
"LCP": ["error", { "maxNumericValue": 2500 }],
"TTI": ["error", { "maxNumericValue": 3500 }]
}
}
}
}
Run with `lhci autorun`. If a deep‑link page exceeds the budget, the build fails, prompting you to split code, lazy‑load assets, or optimize images. ### API Contract Testing for Redirect Endpoints Some deep links rely on an intermediary service (e.g., a link‑shortener or a marketing platform) that issues a 302 with a `Location` header. Contract testing ensures that the service continues to return the expected status code, headers, and body format. **Pact example (Node):**const { Pact } = require('@pact-foundation/pact');
const path = require('path');
const provider = new Pact({
consumer: 'MarketingEmailService',
provider: 'LinkRedirector',
port: 1234,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: 'INFO',
});
describe('Redirector contract', () => {
before(() => provider.setup());
after(() => provider.finalize());
it('returns a 302 to the product page with preserved utm params', async () => {
await provider
.given('a product with ID 42 exists')
.uponReceiving('a request for the short link')
.withRequest({
method: 'GET',
path: '/abc123',
query: { utm_source: 'newsletter', utm_medium: 'email' },
})
.willRespondWith({
status: 302,
headers: { Location: '/product/42?utm_source=newsletter&utm_medium=email' },
});
await provider.verify();
});
});
Running this contract in CI guards against accidental changes to the redirect logic that would break marketing campaigns. ## Edge Cases Only Visible in Production Even with thorough unit, integration, and E2E suites, certain defects surface only when the link is exercised from a real user’s device under unpredictable conditions. Below are the most common production‑only edge cases and how to detect them early. ### 1. Browser Extension Interference Ad‑blockers, privacy extensions, or security tools can strip query parameters they deem “tracking” (e.g., `utm_`, `fbclid`, `gclid`). If your application relies on those parameters for attribution or feature flags, the page may load in a degraded state. **Detection:** - Run your E2E tests with popular extensions installed (uBlock Origin, Privacy Badger, Ghostery) using Playwright’s `launchPersistentContext` with the `args` flag to load extensions. - Verify that core content still appears and that any fallback logic (e.g., reading from `localStorage` when params are missing) works. ### 2. Service Worker Updates Mid‑Navigation A user may have an outdated service worker cached. When they click a deep link, the SW may attempt to serve a stale shell while the network fetches the newest bundle, leading to a mismatch between the UI and the data layer. **Detection:** - Simulate an “update on reload” scenario: register a SW, then change its script URL and reload the page while navigating to a deep link. - Use the `navigator.serviceWorker.getRegistrations()` API to assert that the active SW matches the expected version. - Listen for `statechange` events (`installing`, `activated`, `redundant`) and log any unexpected transitions. ### 3. Private Browsing / Incognito with Ephemeral Storage Some sites store authentication tokens in `localStorage` that are cleared when the window closes. In incognito mode, a deep link may redirect to login, succeed, but then fail to persist the token across page reloads because the storage mode is session‑only. **Detection:** - Run the same deep‑link flow in a persistent context and in an incognito context (`page.context()` in Playwright). - After login, reload the page and verify that the user remains authenticated (check for a protected element or a cookie). ### 4. iOS/Android Web View Quirks When your web app is hosted inside a native WebView (e.g., a React Native wrapper or a Flutter `WebViewWidget`), the user agent string includes `wv`, and certain APIs behave differently (e.g., `navigator.cookieEnabled` may return `false`). Deep links that rely on `document.cookie` for session restoration can break. **Detection:** - Use Playwright’s device emulation (`{ userAgent: '...wv...' }`) or manually set the UA string. - Validate that cookie‑based auth flows still work, or that you have a fallback to `sessionStorage`/`IndexedDB`. ### 5. Safari Intelligent Tracking Prevention (ITP) ITP partitions cookies based on the top‑level domain, which can cause a login cookie set on `login.example.com` to be unavailable when the deep link lands on `app.example.com` if the request is classified as cross‑site tracking. **Detection:** - Test on real Safari or via BrowserStack with ITP enabled. - After login, navigate to a deep link on a different subdomain and verify that the user stays logged in (check for an auth header or a protected endpoint response). ### 6. SameSite Cookie Changes Browsers now treat cookies without an explicit SameSite attribute as `SameSite=Lax`. If your login endpoint sets a cookie without the attribute and your deep link uses a POST form submission (or a fetch with `credentials: 'include'`), the cookie may be blocked on certain navigation types (e.g., a top‑level navigation from a third‑party site). **Detection:** - Explicitly set `SameSite=None; Secure` for cookies that must be sent on cross‑site requests, and verify with `curl -v -b cookie.txt` that the cookie is included. - In automated tests, inspect the `Set-Cookie` header and assert the presence of the SameSite flag. ### 7. Ad‑Blocker Filtering of URLs Containing Certain Keywords Some filter lists block URLs that match patterns like `/track/`, `/affiliate/`, or `/ad/`. If your deep link uses such a path for internal routing (e.g., `/track/order/123`), the request may be cancelled before reaching your server. **Detection:** - Scan your URL patterns against known filter lists (e.g., EasyList) using a tool like `urlfilter-test`. - In E2E tests, enable a known ad‑blocking extension and assert that the networkTest Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free