Why doesn't a Cypress cy.request() appear in the browser's Network tab?
answer
- Ask which process opened the socket
- The browser was never asked to do it
- No browser means no browser security rules
- Interceptors only see traffic the app makes
- Cookies still travel both directions
basics
~20 sCypress makes the request from its Node process, not as a browser XHR, so DevTools never shows it. Because the browser never issues it, CORS is bypassed and cy.intercept does not see it; cookies are still attached and applied.
solid answer
~40 s`cy.request()` is issued by Cypress's own Node process, not by the browser, so there is no XHR for DevTools to record - the Command Log entry is where you inspect it. Three things follow. CORS is bypassed entirely, because no browser security policy sits in the path, so a spec can call an expense-approval API on another host freely. `cy.intercept()` does not match it: `cy.request()` deliberately talks to the real, running endpoint. And the page is untouched - seeding a receipt after `cy.visit()` will not make it render. What *is* shared is the session: Cypress attaches the cookies the browser would have sent, and applies any `Set-Cookie` from the response back onto the browser. It times out per `responseTimeout`, 30000 ms by default.
go deeper
Know that cy.request() talks to a real server and is not a browser request, so DevTools will not show it. Read the response from the Command Log or a .then() callback instead.
Explain the consequences one by one: no CORS, no interception, no effect on the rendered page, but cookies still attached and written back. Name responseTimeout as its timeout.
Be ready to diagnose a suite where setup traffic and application traffic were confused - a stub that never fired, or seeded data that never appeared because nothing reloaded the page.
Own the boundary between setup traffic and evidence: setup calls should stay invisible to the assertions and interceptors that prove what the application itself did.
## Where the request is actually made `cy.request()` is not an XHR and not a `fetch()`. Cypress hands the request description to its own **Node process**, and that process opens the socket, sends the bytes and reads the response. The browser is never asked to do anything, so the browser has nothing to log. The documentation states it plainly: Cypress does not actually make an XHR request from the browser, so you will not see the request inside DevTools. Everything surprising about `cy.request()` follows from that one fact. ## What follows from running outside the browser - **CORS does not apply.** A cross-origin request from a page triggers a preflight `OPTIONS` check and can be refused by the browser. `cy.request()` bypasses CORS entirely, because no browser security policy is in the path. You can call an expense-approval API on another host from a spec loaded on `localhost` and nothing objects. - **`cy.intercept()` does not see it.** Route handlers registered with `cy.intercept()` match traffic the *application* generates. A `cy.request()` goes to the actual, running endpoint and is not matched, which is the intended design: `cy.request()` exists for talking to a real server. - **The DevTools Network tab stays empty.** If you are hunting for the call, the Command Log entry for `request` is where it lives; clicking it prints the request and the response to the console. - **The page does not change.** `cy.request()` does not navigate, does not re-render, and does not refetch anything the app already loaded. Seeding a receipt after `cy.visit()` will not make it appear on screen unless something reloads the data. ## The one thing that *is* shared: cookies This is the part people get wrong in both directions. Running in Node does **not** mean running without the browser's session. 1. Before sending, Cypress attaches the cookies that the browser would have attached had the request come from the page, pulled from the cookie jar by domain match. 2. If the response carries a `Set-Cookie` header, Cypress sets those cookies back on the browser. 3. When it follows a redirect, it repeats both steps at every hop, so a chain that sets a cookie on the first response and requires it on the second behaves the way it would in a real browser. Domain matching is worth remembering: a cookie set without `hostOnly: true` is a domain cookie, so it can be attached to a `cy.request()` aimed at a *different* host that domain-matches it — a sibling subdomain, for example. ## How it compares with the browser's own traffic | | `cy.request()` | traffic the app itself makes | |---|---|---| | issued by | Cypress's Node process | the browser | | visible in DevTools Network | no | yes | | subject to CORS and preflight | no | yes | | matched by `cy.intercept()` | no | yes | | sends browser cookies | yes | yes | | affects what is rendered | no | yes | | default timeout | `responseTimeout`, `30000` ms | page and command timeouts | ## Practical consequences for a seeding step - Use it to put state in place before the app is asked to display it: create the expense report, approve it, then `cy.visit()` the detail page. Ordering matters precisely because `cy.request()` does not touch the page. - Assert on the yielded response object — `status`, `body`, `headers`, `duration` — but know that `cy.request()` runs a chained assertion **once and does not retry**. There is no element to wait for, so retry-ability would be meaningless; if the endpoint is not ready, poll it deliberately rather than expecting `.should()` to keep trying. - `response.body` is parsed into a JavaScript object only when the response's `Content-Type` ends in `json`. Otherwise you get a string. That depends entirely on what the server sends back, not on what you sent. - A non-2xx/3xx status fails the command by default; `failOnStatusCode: false` is how you assert on a `403` for an expense report the signed-in user may not see. ## Why this design exists at all A browser-issued request would be subject to every restriction the application under test is subject to — same-origin policy, preflights, the app's own service worker and interceptors — which is exactly wrong for a step whose job is to *set up* the world before the application is involved. Putting the request in Node gives the test a channel that behaves like a well-behaved HTTP client with the browser's cookie jar attached, and keeps the setup traffic out of the evidence you collect about what the application actually did.
- If cy.request() bypasses the browser, how does a cookie set by the response reach the app?Cypress writes it back. When a `cy.request()` response carries a `Set-Cookie` header, Cypress sets those cookies on the browser's cookie jar, so a subsequent `cy.visit()` or an application request sends them. It does the same at every hop while following a redirect chain.
- Why doesn't .should() retry an assertion chained onto a Cypress cy.request()?There is nothing to re-query. `cy.request()` resolves once with a response object; chained assertions run against that single value and are not retried. Retry-ability exists for queries that can re-read the DOM. If an endpoint may not be ready yet, write an explicit polling loop rather than leaning on `.should()`.
cy.request() is the test walking round to the server's back door with the browser's keys in its pocket: it presents the same cookies the browser would have, but the browser's own log of who came and went never records the visit.
saying these in an interview costs you the question
- Expects cy.intercept() to stub a cy.request() call
- Says cy.request() is blocked by CORS like page traffic
- Hunts for the call in the DevTools Network tab
- Assumes a cy.request() after cy.visit() re-renders the page
- Thinks running in Node means the browser session is not used