v0 + Next.jsUser-controlled proxy URL

v0 Next.js API proxy fetches a user-supplied URL: how to fix the SSRF risk

When a Next.js API route passes a caller-supplied URL straight to server-side `fetch`, the caller may be able to make the server contact destinations that their own browser cannot reach. Replace the arbitrary URL with a named, configured upstream action. Allow only required methods and headers, and enforce destination and egress checks before every outbound request.

This guide covers check 04: Input validation and error handling; check 06: Performance and scalability in the Zenveus Production Readiness Standard.

For builders

What this means, in plain words

A proxy lets your server fetch another website for the app. If visitors choose any destination, they may make it contact systems that should be private. Replace arbitrary URLs with approved actions and verify that blocked destinations receive no request.

A scoped repair request

Using an AI builder? Paste this

Use this prompt in Lovable, Cursor, Replit, or Claude Code with the relevant server files available.

Audit my v0 Next.js proxy route for SSRF. Replace client-selected URLs with server-defined upstream actions; check redirects, DNS resolution, response size, timeouts, and deployment egress policy. Work on a branch with synthetic data and mocked external services. Show the smallest diff, identify required adapters and deployment settings, and add allowed and denied tests that prove side effects cannot happen before checks pass. Do not disable security checks to make a test pass.

The same failure may appear as

  • Next.js API proxy accepts any URL
  • Server fetch reaches an internal host
  • A redirect changes a safe URL into a private destination
  • Caller-controlled headers are forwarded upstream

Find the failure layer

Run these checks before rewriting anything

Each check removes a class of causes. Keep the first failing result, its timestamp, and the production log beside it.

01

URL input

Find every place the request body, query, or header can set the outbound destination. Record any user-controlled method and forwarded headers.

If this failsAn arbitrary URL reaches server fetch; replace it with a server-owned action map before opening the route.
02

Network reach

Determine whether the route is deployed, who can call it, what networks the server can reach, and whether a gateway or egress firewall constrains it. Source code alone does not establish live reachability.

If this failsUnknown egress leaves internal reachability unresolved; inspect the deployed network policy and isolate the worker.
03

Blocked destinations

Use a permitted mock API and a fake internal HTTP endpoint. Confirm that private, loopback, link-local, metadata-style, redirected, and re-resolved destinations are rejected before a request reaches the fake internal service. Do not probe live third-party systems.

If this failsAny contact with the mock internal service means validation was bypassed; trace redirects and resolved addresses before retesting.
04

Response limits

Limit time, bytes, and content types. Do not relay arbitrary upstream errors, credentials, or headers to the caller.

If this failsUnbounded bytes or forwarded credentials widen abuse; restrict headers, methods, response size, and duration.

Ranked diagnosis

Common root causes, in the order we would test them

01

Arbitrary upstream selection

The client provides a full URL instead of selecting a server-defined action.

02

Syntax-only validation

A valid URL can still resolve or redirect to an internal address.

03

No egress boundary

The server can reach destinations unrelated to the intended product integration.

04

Overbroad forwarding

Caller-selected methods, headers, or response bodies make the proxy more powerful than required.

Step-by-step repair

How to fix this in your app

Replace the handler in app/api/proxy/route.ts that accepts a complete URL. If the integration needs arbitrary websites, use a dedicated outbound gateway rather than this fixed-upstream pattern.

Production safety ruleNever disable access controls, expose service keys, or add wildcard CORS as a routine shortcut.

01

Replace the URL parameter with a named action

Remove client-supplied destination, method, and forwarding headers. Map an operation such as health to one literal HTTPS endpoint you control or trust. Fail deployment until that endpoint is configured.

02

Reject redirects and bound each fetch

Set redirect to error and use a request timeout. Forward only server-selected headers. For larger responses, read a stream with a byte ceiling; a Content-Length check alone is insufficient.

03

Enforce destination policy outside the application

Restrict outbound traffic to the required upstream. If URLs remain dynamic, validate all resolved IPv4 and IPv6 addresses and enforce the same policy when connecting; a separate DNS check followed by ordinary fetch leaves a rebinding gap.

04

Use an isolated upstream fixture

Test one permitted destination, an unknown action, and a permitted endpoint that redirects. Confirm no request reaches the redirect target. Do not test cloud metadata addresses on a production host.

Implementation example

Next.js: one fixed health-check upstream

const HEALTH_URL = 'https://api.example.com/health';
export async function POST(request: Request) {
  const body = await request.json().catch(() => null);
  if (!body || body.action !== 'health') {
    return new Response('Unsupported action', { status: 400 });
  }
  try {
    const upstream = await fetch(HEALTH_URL, {
      method: 'GET', redirect: 'error', cache: 'no-store',
      signal: AbortSignal.timeout(5000)
    });
    return Response.json({ healthy: upstream.ok });
  } catch {
    return new Response('Upstream unavailable', { status: 502 });
  }
}

Replace api.example.com with your actual fixed upstream. This deliberately returns a status result instead of relaying arbitrary content. Add caller authorization where required and an egress policy; this is not an arbitrary-URL sanitizer.

Prove the repair

How to check that the fix worked

Run these checks with synthetic data in your test environment, then repeat the relevant acceptance checks after deployment.

  • Known action: only the configured host receives a GET.
  • Unknown action or supplied URL: 400, with zero outbound requests.
  • Redirecting upstream: 502 and no request to its redirect target.
  • Slow upstream: request fails within the configured timeout.

If the check still fails

If the server still contacts unexpected hosts, trace other fetch calls and redirects. A host allowlist in code cannot replace network restrictions when the upstream or DNS is outside your control.

When the built-in AI fix makes it worse

Recover one reproducible failure.

Pause generated changes, restore a known working branch, and capture one failing request with its logs. Change one layer and rerun the allowed and denied checks before proceeding.

Engineering handoff

What Zenveus checks when the quick fix is not enough

We trace one production request through the complete path, isolate the failing boundary, and leave behind evidence your team can repeat.

Upstream contract

Server-owned action maps, credentials, methods, and response handling.

Destination changes

Resolved addresses, DNS changes, redirects, and every outbound hop.

Deployment network

Worker egress rules and internal or metadata-network reachability.

Blocked-request proof

Mock internal-service logs alongside allowed upstream requests.

Typical repair pattern

Only configured upstream actions can leave the server

A permitted mock API works while private, redirected, and arbitrary destinations fail before network contact.

Evidence left behind
  • Root-cause note
  • Verified production check
  • Rollback and prevention steps

Before the next release

Prevent this failure from returning

Review every new server-side fetch that accepts browser input.
Keep egress rules and upstream allowlists under version control.

Clear answers

Questions teams ask before they touch production

01Is checking that a URL starts with `https://` enough?

No. DNS, redirects, alternate address forms, and network reachability still matter.

02Can the browser's CORS policy stop server-side SSRF?

No. The outbound request originates on the server, outside browser CORS enforcement.

03Should I blacklist a few internal hostnames?

Prefer a short allowlist of named upstream services plus network isolation. A small blacklist misses other internal addresses and redirect paths.

04How do I know the fix worked in my app?

Run the verification checks on this page against your test environment, then repeat the relevant checks after deployment. Example code needs your app's authentication, data model, and configuration; reading the guide alone does not verify your deployment.

Free next step

Check the boundary before you hand it over

The free tool helps you inspect this symptom. Its result does not establish whether the whole app is production ready.

The Verdict

Know whether the symptom is contained or structural.

We can see the symptom from here. What we cannot tell you from outside is whether it is contained or structural. A scanner collects evidence. A named senior engineer makes the decision. For $299, a named senior engineer reads your code and signs a written Verdict against the nine checks in the Zenveus Production Readiness Standard. The 48-hour clock begins when the required access and context are available. If the report does not give your developer a list they can act on, you do not pay.

The 48-hour clock starts when the required access and context are available.

Scroll to Top