ScreenshotNeo

BlogHow-to

How to Fix Cypress Page Not Found Errors When Manual Loading Works

Diagnose Cypress 404s by comparing the exact URL, redirects, authentication, SPA fallback, and proxy path instead of guessing.

By the ScreenshotNeo team1 October 20268 min read

Short answer: a page that works when you type it into a browser can still return 404 in Cypress because Cypress requested a different URL, followed a redirect, lacked an authenticated session, reached a server without the SPA fallback, or used a different proxy path. Capture the exact request and response first; then fix the branch that the evidence identifies.

This guide gives a repeatable diagnostic sequence for cy.visit(), with runnable Cypress, cURL, Python, and Node.js examples. It also explains when an old localhost proxy workaround is relevant and when it is not.

1. Reproduce and record the failing request

Start with the smallest test that fails. Log the URL you intend to visit and make the base URL explicit.

describe('inventory route', () => {
  it('opens the inventory page', () => {
    const path = '/inventory.html';
    cy.log(`baseUrl: ${Cypress.config('baseUrl')}`);
    cy.log(`path: ${path}`);
    cy.visit(path);
  });
});

Check the resolved URL character by character:

  • scheme: http versus https
  • hostname and port
  • path spelling and case
  • trailing slash
  • query string and hash
  • whether the path is absolute or relative to baseUrl

For example, with baseUrl: "https://www.example.test/", cy.visit('/inventory.html') should target https://www.example.test/inventory.html. A different configured base URL, an extra path prefix, or a missing slash can send Cypress elsewhere.

2. Verify baseUrl and path resolution

Put the setting in the Cypress configuration used by the failing command, not only in a local file that another project configuration overrides.

// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    baseUrl: 'https://www.example.test',
    setupNodeEvents(on, config) {
      return config;
    }
  }
});

Prefer a path relative to baseUrl when the test is intentionally tied to that application:

cy.visit('/inventory.html');

Use a complete URL when you are deliberately testing another origin:

cy.visit('https://www.example.test/inventory.html');

Do not assume a browser address-bar visit used the same URL. Copy the address exactly, including a trailing slash and query parameters, and compare it with Cypress’s Command Log and browser network panel.

3. Inspect status codes and redirects

The final page you see manually may be the result of a redirect. The original path can still return 404, then redirect to a home page or login page. Inspect the response chain rather than judging only the rendered screen.

Browser and Cypress inspection

Open the Cypress browser’s DevTools, reproduce the failure, and inspect the first document request. Record status, Location, request headers, and response body. A Cypress failure that says “page not found” is more actionable when you know whether the first response was 404, 301, 302, 401, 403, or a network error.

Cypress documents cy.visit() behavior and options in its API reference: cy.visit() documentation.

cURL: follow and stop redirects separately

# Show the first response, without following redirects
curl -sS -D - -o /dev/null 'https://www.example.test/inventory.html'

# Follow redirects and show the final response headers
curl -sS -L -D - -o /dev/null 'https://www.example.test/inventory.html'

If the first command reports 404 but the second ends at a 200 home page, the route is not actually being served as requested. Fix the server route or test the destination Cypress is supposed to use.

Python: print every response in the redirect chain

import requests

url = 'https://www.example.test/inventory.html'
response = requests.get(url, allow_redirects=True, timeout=30)

for item in response.history:
    print(item.status_code, item.url, 'Location=', item.headers.get('Location'))

print(response.status_code, response.url)
print(response.text[:500])

Node.js: inspect redirects with fetch

const url = 'https://www.example.test/inventory.html';
const res = await fetch(url, { redirect: 'manual' });
console.log('status:', res.status);
console.log('location:', res.headers.get('location'));
console.log('url:', res.url);

4. Check whether authentication changes the route

A protected route may redirect an anonymous Cypress session to a login page or return an error, while your normal browser already has a cookie. The old community report that inspired this symptom proposed login as a possible explanation for its inventory route; treat that as a site-specific hypothesis, not a universal Cypress rule.

Confirm the difference by testing in a private browser window and by checking the request’s cookies and response status. If authentication is required, establish the session before visiting the protected path.

Example with a programmatic login

describe('authenticated inventory', () => {
  beforeEach(() => {
    cy.request('POST', '/api/login', {
      username: Cypress.env('TEST_USER'),
      password: Cypress.env('TEST_PASSWORD')
    }).then(({ body }) => {
      window.localStorage.setItem('access_token', body.token);
    });
  });

  it('loads the protected route', () => {
    cy.visit('/inventory.html');
    cy.contains('Inventory').should('be.visible');
  });
});

Adapt the login mechanism to your application. Do not put real credentials in source control. If the application uses cookies, use the application’s supported login flow or set a test cookie in a controlled environment, then verify that the cookie is sent to the target host.

Single-page applications commonly use browser history routes such as /todos/42. During development, the dev server may return index.html for that path. A production static server can instead look for a physical todos/42 file, return 404, and never give the client router a chance to run.

Vue CLI’s deployment guidance describes this failure mode and the need for a server fallback: Vue CLI deployment.

Fallback examples

Configure your production server to serve the application entry point for unknown document requests while still returning real 404s for missing assets. For an Nginx deployment, the pattern is:

location / {
  try_files $uri $uri/ /index.html;
}

For an Express server, place the fallback after static assets and before the final 404 handler:

const express = require('express');
const path = require('node:path');

const app = express();
const dist = path.join(__dirname, 'dist');

app.use(express.static(dist));
app.get('*', (req, res) => {
  res.sendFile(path.join(dist, 'index.html'));
});

app.listen(3000);

Only use this remedy when the application uses history-mode routing. Hash routes such as /#/todos/42 do not require the server to understand the route path.

6. Compare headers, cookies, and user agents

If the URL and status are correct, compare the request context. A reverse proxy or application can vary its response by:

  • Host and Origin
  • authentication cookies or authorization headers
  • user agent
  • accepted language or encoding
  • custom tenant headers

Use a controlled diagnostic endpoint or server access logs to compare a successful manual request with Cypress. Avoid “fixing” the problem by copying browser-only headers blindly; identify which missing value selects the correct application route.

7. Investigate localhost and proxy behavior only with matching versions

A Cypress issue from 2018 reported intermittent 404 behavior while visiting separate localhost ports with Cypress 3.0.1, Windows 10, and Chrome. A later participant associated a similar case with Chrome bypassing Cypress’s proxy for loopback addresses and described --proxy-bypass-list=<-loopback> as a workaround.

This is historical, environment-specific evidence. Do not apply that flag as a default fix. First reproduce the failure with current Cypress and browser versions, confirm that the failing host is loopback, and verify the request path in DevTools. If the request never reaches the expected port, then test the documented browser launch argument in an isolated CI configuration and remove it if it does not change the network trace.

8. A decision tree for the next diagnostic step

Evidence Likely branch Next action
Resolved URL differs Base URL or path construction Correct baseUrl, slash, host, port, or path.
First response is 3xx Redirect or login gate Inspect Location, cookies, and final destination.
401 or 403 Missing session or permission Authenticate in the test and verify the sent credentials.
404 for a client route Missing SPA fallback Serve index.html for history-mode document requests.
Request reaches the wrong localhost port Proxy or loopback path Check current browser/Cypress versions and proxy settings.
Network error before HTTP DNS, TLS, server readiness, or firewall Check service startup, certificate trust, and connectivity.

9. Common errors and fixes

“Cypress failed to load the page” with a 404

Read the exact URL from the error and network panel. Correct a mismatch first; if it is a deep SPA route, add the server fallback.

The test lands on the home page

The requested path may redirect there. Inspect the first response and Location header; do not treat the home page as proof that the original route succeeded.

Manual browser works, Cypress is logged out

Your normal profile has state that Cypress does not. Add a deterministic login step or test cookie setup and assert that the protected API responds successfully.

Only production fails

Compare production’s routing layer with the development server. Static hosting often needs an explicit history fallback.

Only CI fails

Log the resolved URL, browser version, Cypress version, proxy variables, DNS result, and service readiness. CI may use a different host, port, certificate, or authentication secret.

A proxy flag appears to fix it locally

Record the exact browser and Cypress versions and confirm the request path changed. Keep the workaround scoped to the affected environment because the historical report does not establish a current universal fix.

10. Reliability and performance checklist

  • Start the application and wait for a health endpoint before Cypress begins.
  • Use stable test URLs and avoid relying on a developer’s browser profile.
  • Assert the final pathname and a page-specific element after cy.visit().
  • Capture the first HTTP status and redirect location in CI logs.
  • Keep server fallback rules narrow enough that missing JavaScript and CSS still fail clearly.
  • Use one consistent base URL per test environment.
  • Do not increase timeouts to hide a wrong route; a timeout does not repair a 404.

11. Or skip the browser setup

If your goal is to obtain a clean screenshot of a URL rather than debug Cypress itself, ScreenshotNeo provides a single screenshot API request. See the ScreenshotNeo documentation for options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor, and other MCP clients take screenshots with take_screenshot, inspect pages with get_page_info, and create PDFs with capture_pdf. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

12. FAQ

Does a Cypress 404 always mean the application is down?

No. It can indicate a different URL, redirect, missing login state, missing SPA fallback, or proxy path. Inspect the request before deciding.

Should I add a trailing slash?

Match the route your server actually serves. Test both forms with headers and redirects; do not add a slash solely because the error message shows one.

Can increasing pageLoadTimeout fix a 404?

No. Timeouts affect waiting; they do not change an HTTP status or server route.

When should I use a hash route?

Hash routing can avoid server-side deep-link fallback requirements, but changing routing is an application decision. Configure the server fallback when history-mode URLs are required.

What should I include in a bug report?

Include Cypress and browser versions, operating system, exact resolved URL, first status, redirect chain, authentication state, base URL, and whether the failure occurs locally, in CI, or on a particular localhost port.