localhost:3001 is port 3001 on your own computer, and it's usually the "second" dev server: the place Next.js lands when 3000 is already taken, or the API that runs beside a frontend on 3000. BrowserSync's control panel and Uptime Kuma also default to 3001. If you see ERR_CONNECTION_REFUSED, nothing is listening there right now.
What runs on port 3001
| What | Why it's on 3001 |
|---|---|
| Next.js, Nuxt, Remix dev server | Moved automatically because 3000 was busy |
| Create React App | Asks "Would you like to run the app on another port instead?" and picks 3001 |
| Express / NestJS / Fastify API | Convention when the frontend owns 3000 |
| BrowserSync UI | Control panel next to the proxied site on 3000 |
| Uptime Kuma | Default port of the self-hosted monitor |
Fix "localhost:3001 refused to connect"
- Check where the app actually is. If your app only went to 3001 because 3000 was busy last time, it may be back on 3000 now. Read the
Local:URL your dev server prints. - API not started. In a monorepo,
npm run devin the frontend package won't start the API. Use your workspace runner (turbo dev,npm run dev --workspaces,docker compose up) or start the API in its own terminal. - Docker Compose. Each service needs its own mapping, for example
"3001:3001"for the API. From another container, call the API by service name (http://api:3001), notlocalhost. Inside a container,localhostis that container.
Find and free a busy port 3001
# macOS / Linux
lsof -i :3000 -i :3001
kill -9 $(lsof -ti :3001)
# Windows
netstat -ano | findstr :3001
taskkill /PID <pid> /F
If you keep ending up on 3001 by accident, the real fix is to find what holds 3000, often a dev server from another project or a crashed next dev. To fix the port on purpose: PORT=3001 npm start, next dev -p 3001, or app.listen(process.env.PORT || 3001) in Express.
Frontend on 3000, API on 3001
Browsers treat localhost:3000 and localhost:3001 as different origins, so fetch('http://localhost:3001/api') from the frontend triggers CORS. Two clean options:
- Proxy through the frontend dev server. Vite's
server.proxy, Next.jsrewrites, or CRA's"proxy": "http://localhost:3001"inpackage.json. The browser sees one origin and cookies just work. - Enable CORS on the API for
http://localhost:3000only, not*, especially if you send cookies.
Open localhost:3001 from another device
Bind the server to all interfaces (HOST=0.0.0.0, next dev -H 0.0.0.0, or app.listen(3001, '0.0.0.0')), then open http://YOUR-LAN-IP:3001 from a device on the same network. If the frontend calls the API as http://localhost:3001, it breaks on the phone, because the phone's localhost is the phone. Use the proxy approach above or a relative URL.
Share 3001 or receive webhooks on it
When the frontend is on 3000 and the API on 3001, webhooks from Stripe, GitHub or Shopify should go straight to the API:
relay forward --bucket my-api http://localhost:3001/webhooks
The bucket has a permanent public URL that you paste into the provider once. Each webhook is delivered to port 3001 and logged, so you can replay it while you iterate. See receive webhooks on localhost.
To share a running app, tunnel it:
relay connect --name my-app http://localhost:3001
For a split frontend and API, tunnel the frontend and let its dev-server proxy reach the API locally. That way you need only one public URL.
