# Fix Next.js “Blocked cross-origin request to Next.js dev resource”

> Fix Next.js blocked dev requests: allow only the exact browser hostname for dev resources and Server Actions, with development-only settings.

Source: https://layerv.ai/guides/fix-nextjs-blocked-cross-origin-dev-request/

---

[← All guides](https://layerv.ai/guides/) 

# Fix Next.js “Blocked cross-origin request to Next.js dev resource”

Content updated October 25, 2026.

## Why does next dev block requests when I open my app through a tunnel?

Share the running next dev build with qURL™: hot reload and Server Actions work through the link while your app stays on your machine with no public URL. Set QURL\_DEV\_ORIGIN\_HOST to the exact browser hostname for dev resources and Server Actions, then restart. A Vercel preview fits when you need a deployed build that stays online after you close your laptop.

## What you need

- A Next.js app running with next dev, opened through a tunnel or another hostname
- Your next.config.js (or next.config.ts)

## Steps

1. ### Read the origin from the log  
next dev logs “Blocked cross-origin request to Next.js dev resource … from “host”” and answers 403\. The exact wording varies by release. Next.js 16.1 and earlier only warned: “Cross origin request detected from … to /\_next/\* resource.”
2. ### Add the tunnel’s hostname to allowedDevOrigins  
Next.js matches only the hostname of the request’s Origin, so write entries without https:// and without a port. For dev resources and Server Actions, set QURL\_DEV\_ORIGIN\_HOST to the exact r\_….qurl.site hostname from the browser address bar, without https://, a port or a path. The configuration below adds only that hostname in development. A wildcard such as \*.qurl.site would also let other publishers’ pages load your dev resources. Unlike allowedDevOrigins, serverActions.allowedOrigins also applies to production builds, so keep the development guard and never use a whole-domain wildcard there. For next.config.ts or next.config.mjs, export the same object with export default instead of module.exports.  
```  
// next.config.js
module.exports = {
  allowedDevOrigins:
    process.env.NODE_ENV === 'development' && process.env.QURL_DEV_ORIGIN_HOST
      ? [process.env.QURL_DEV_ORIGIN_HOST]
      : [],
  experimental: {
    serverActions: {
      // Development only: the hostname from the browser bar, without https:// or a port.
      allowedOrigins:
        process.env.NODE_ENV === 'development' && process.env.QURL_DEV_ORIGIN_HOST
          ? [process.env.QURL_DEV_ORIGIN_HOST]
          : [],
    },
  },
}
```
3. ### Create the qURL link  
Publish the running dev server, make a link, and send it to the recipient. They open it and keep that page open while you configure their exact browser origin in the next step. The first publish creates this device’s identity and needs no account. Each link opens once and expires within 24 hours.  
```  
qurl publish http://127.0.0.1:3000
qurl share <CRID>
```
4. ### Restart next dev  
Ask the recipient to send you the exact browser hostname from the page they just opened. They must keep that page open; do not open their single-use link yourself. Set QURL\_DEV\_ORIGIN\_HOST to that hostname (replace r\_your-link below), restart next dev, and ask them to reload the same page. This configuration trusts one recipient origin at a time; update it for each new browser session. Next.js reads allowedDevOrigins at start-up. Bind next dev to 127.0.0.1 so your app stays on this computer and nothing listens on your network.  
```  
QURL_DEV_ORIGIN_HOST=r_your-link.qurl.site npx next dev -H 127.0.0.1
```

## What the recipient sees

With the origin allowed, the page and its dev resources load through the link, and the hot reload socket can connect.

## Which tool fits sharing a next dev server?

| Option                                                                                    | Choose it when                                                                                                                                                                     | Trade-off                                                                                                                                                                                    |
| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [qURL](https://layerv.ai/research/qurl-behaviour/)                                        | qURL fits when particular people should try the live next dev build: hot reload and Server Actions work through the link, and the app stays off the public internet.               | Keep your computer on while they use qURL. In development, add each recipient session’s exact browser hostname to allowedDevOrigins and serverActions.allowedOrigins, then restart next dev. |
| [Vercel preview deployment](https://layerv.ai/compare/deploy-preview-vs-share-localhost/) | A Vercel preview deployment fits when reviewers come back over days: it runs a production build, so allowedDevOrigins does not apply, and it stays online with your laptop closed. | A Vercel preview is a deployed copy, so later edits need another deploy, and new Vercel projects enable deployment protection by default; check access settings before sharing.              |
| [ngrok](https://layerv.ai/compare/qurl-vs-ngrok/)                                         | ngrok fits when you want one stable URL for next dev: your ngrok dev domain stays the same, so one allowedDevOrigins entry lasts.                                                  | ngrok needs an account and authtoken, the URL is public, and free-plan browser visitors see a warning page first.                                                                            |
| [Cloudflare Quick Tunnels](https://layerv.ai/compare/qurl-vs-cloudflare-quick-tunnels/)   | A Cloudflare Quick Tunnel fits a no-account look at next dev: allow its exact assigned trycloudflare.com hostname, then update the entry and restart next dev each run.            | A Cloudflare Quick Tunnel URL is public unless you set --allowed-mail, and Quick Tunnels do not support Server-Sent Events, so a route handler that streams them stalls.                     |

## Troubleshooting

| If you see                                                                                                             | Do this                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| qurl publish says the local app is not accepting TCP connections                                                       | Start the app first and check that curl works on the same address. qurl publish connects to localhost as 127.0.0.1, so if your app listens only on IPv6 (on macOS or Linux, lsof -iTCP -sTCP:LISTEN -nP shows \[::1\]), publish 'http://\[::1\]:PORT' instead, or start the dev server with --host 127.0.0.1 and publish that address. Neither requires listening on your LAN. Keep the quotes: zsh treats the brackets as a pattern.                         |
| The recipient sees “Access link invalid”                                                                               | The link was already used or has expired. Run qurl share again and send the new link.                                                                                                                                                                                                                                                                                                                                                                         |
| The recipient sees “Resource not found”                                                                                | The resource was deleted after the link was made. Publish the app again, then run qurl share and send the new link.                                                                                                                                                                                                                                                                                                                                           |
| publish rejects http://127.0.0.1 with “only HTTPS URLs are allowed”, or asks you to log in                             | You have a CLI older than 3.0\. On macOS run brew update, then brew upgrade qurl; elsewhere install the latest release again. After updating, qurl --version should print 3.4.1 or later.                                                                                                                                                                                                                                                                     |
| publish prints no Access: line, or the Access: line says public and you did not ask for public                         | With no Access: line, the CLI is older than 3.4.1: on macOS run brew update, then brew upgrade qurl; elsewhere install the latest release again. A resource that an older CLI published is public unless you published it with --private, and anyone who has the CRID of a public resource can open it. To make a public resource private, run qurl delete <CRID> and publish again with qurl 3.4.1 or later. The new resource is private and has a new CRID. |
| publish says this Linux session “can’t reach a systemd user manager”                                                   | You are in a container or sandbox without a user service manager. Run qurl publish with --foreground; sharing lasts as long as that process runs.                                                                                                                                                                                                                                                                                                             |
| publish says “Quota Exceeded” with “protected resource limit reached”, or “Your qURL account has reached a plan limit” | You can see this without an account: a device with no account can keep up to 3 active resources, with links that last up to 24 hours. Run qurl list --status active, then qurl delete <CRID> for any you no longer need (it asks you to confirm; --yes skips that). qurl stop keeps the resource, so it does not free a slot.                                                                                                                                 |
| Server Actions fail with “Invalid Server Actions request”                                                              | Set QURL\_DEV\_ORIGIN\_HOST to the exact r\_….qurl.site browser hostname and restart next dev. The development-only configuration above adds that host to experimental.serverActions.allowedOrigins; do not add \*.qurl.site there.                                                                                                                                                                                                                           |
| The browser origin is still blocked                                                                                    | Set QURL\_DEV\_ORIGIN\_HOST to the exact hostname from the recipient’s browser address bar, without https://, a port or a path. Set it in the same shell used to restart npx next dev.                                                                                                                                                                                                                                                                        |

## Frequently Asked Questions

Why did this start in Next.js 16.2? 

Earlier releases warned about cross-origin dev requests and allowed them. Next.js 16.2 enforces the dev-origin guard by default, so a tunnel’s hostname must now be listed in allowedDevOrigins.

Does this fix work on Next.js 16.4? 

Yes. Use allowedDevOrigins for dev resources and hot reload. For Server Actions, use the development-only exact-host configuration above: the browser is on r\_….qurl.site while the app receives c-….qurl.site.

Does allowedDevOrigins affect production? 

No. allowedDevOrigins applies only to next dev. serverActions.allowedOrigins also applies to production builds, which is why the example enables the exact browser hostname only when NODE\_ENV is development.

Which pattern do I add for qURL? 

For dev resources and Server Actions, put only the exact browser hostname, such as r\_your-link.qurl.site, in QURL\_DEV\_ORIGIN\_HOST and keep the development-only guard shown above. Trusting a wildcard there would also trust other publishers’ pages.

Who else can reach my app while the link is open? 

Opening the link admits the network address it was opened from for the session, which lasts 1 hour by default. Other devices that share that public IP address, such as people on the same office or café network or VPN, can reach the app until the session ends. By default a link admits whoever opens it; access rules set through the API, such as IP allow and deny lists, can restrict that. Keep your app’s own sign-in for anything sensitive.

## Related Resources

[Guide: Allow a tunnel’s hostname in your dev server →](https://layerv.ai/guides/allow-tunnel-hostname-in-dev-server/) [Guide: Share a dev server with a client →](https://layerv.ai/guides/share-dev-server-with-client/) [Next.js docs: allowedDevOrigins ↗](https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins) [Every localhost tunnel, compared →](https://layerv.ai/compare/localhost-tunnels/)

## Publish your app now

Install the CLI, then run one command. No sign-up, no API key.

[Install the CLI ↗](https://layerv.ai/cli/)



---

*This markdown version is auto-generated from [https://layerv.ai/guides/fix-nextjs-blocked-cross-origin-dev-request/](https://layerv.ai/guides/fix-nextjs-blocked-cross-origin-dev-request/) for AI agents. Curated agent resources: [llms.txt](https://layerv.ai/llms.txt). For the full interactive experience, visit the HTML version.*

