DEV Community

Deep Fix
Deep Fix

Posted on

Fix CORS Errors in Node.js Fastify – Complete Step‑by‑Step Guide for Developers

How to Fix CORS Errors in Node.js Fastify

Cross‑Origin Resource Sharing (CORS) errors are one of the most common roadblocks when building APIs with Fastify. This guide walks you through the root causes, provides a solid Fastify‑compatible solution, and shows you how to test it locally and in production.


Why CORS Errors Occur

When a browser requests a resource from a different origin (scheme, host, or port) than the one serving the page, the server must explicitly allow that request via CORS headers. If the headers are missing or malformed, the browser blocks the response and logs an error such as:

Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Enter fullscreen mode Exit fullscreen mode

Fastify does not add CORS headers automatically; you have to enable them via a plugin.


Step‑by‑Step Fix Using fastify-cors

1️⃣ Install the official plugin

npm install fastify-cors
Enter fullscreen mode Exit fullscreen mode

2️⃣ Register the plugin with appropriate options

// server.js
const fastify = require('fastify')({ logger: true })

// Enable CORS for all origins (development only)
fastify.register(require('fastify-cors'), {
  origin: '*',            // <- allow any origin
  methods: ['GET','POST','PUT','DELETE','OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true
})

fastify.get('/ping', async (request, reply) => {
  return { msg: 'pong' }
})

fastify.listen({ port: 3000 }, err => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info('Server listening on http://localhost:3000')
})
Enter fullscreen mode Exit fullscreen mode

Tip: In production replace origin: '*' with a whitelist array, e.g., origin: ['https://app.example.com'].

3️⃣ Verify the headers locally

curl -i -X GET http://localhost:3000/ping
Enter fullscreen mode Exit fullscreen mode

You should see:

HTTP/1.1 200 OK
access-control-allow-origin: *
access-control-allow-credentials: true
content-type: application/json; charset=utf-8
...
Enter fullscreen mode Exit fullscreen mode

4️⃣ Test from the browser

Create a simple HTML file:

<!DOCTYPE html>
<html>
<head><title>CORS Test</title></head>
<body>
<script>
fetch('http://localhost:3000/ping')
  .then(r => r.json())
  .then(console.log)
  .catch(console.error)
</script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Open it in Chrome; the console should log {msg:"pong"} without any CORS warnings.


Advanced Scenarios

✅ Pre‑flight Requests (OPTIONS)

For non‑simple requests (e.g., custom headers or methods other than GET/POST), browsers send an OPTIONS pre‑flight. Fastify‑cors handles this automatically, but you can customize the response:

fastify.register(require('fastify-cors'), {
  preflightContinue: false,
  optionsSuccessStatus: 204
})
Enter fullscreen mode Exit fullscreen mode

🔐 Credentialed Requests

When credentials: true is set, you must not use origin: '*'. Instead specify exact origins:

fastify.register(require('fastify-cors'), {
  origin: ['https://dashboard.example.com'],
  credentials: true
})
Enter fullscreen mode Exit fullscreen mode

🛡️ Security Hardening

  • Limit allowed methods to only those you expose.
  • Restrict headers to the minimal set needed.
  • Enable rate‑limiting (e.g., fastify-rate-limit) to mitigate abuse.

Common Pitfalls & Debugging Tips

Symptom Likely Cause Fix
CORS header ‘Access-Control-Allow-Origin’ missing Plugin not registered or placed after route definitions Register fastify-cors before any routes.
Preflight response is 404 No OPTIONS handler – ensure fastify-cors is loaded.
Credentials blocked Using origin: '*' with credentials: true. Set explicit origin list.
Still blocked after changes Browser cache – clear or use incognito.

Use fastify.log.info(request.headers.origin) inside a route to inspect the incoming origin when troubleshooting.


Deploying to Production (Docker Example)

# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

Build and run:

docker build -t fastify‑api .
docker run -p 8080:8080 fastify‑api
Enter fullscreen mode Exit fullscreen mode

Make sure the CORS configuration matches your production domain.


Quick Recap

  1. Install fastify-cors.
  2. Register it before routes.
  3. Use explicit origins in production.
  4. Test both simple and pre‑flight requests.
  5. Harden the policy for security.

Resources & Tools


Get the Ready‑Made Fix

If you prefer a plug‑and‑play solution, you can download a pre‑configured Fastify starter that already includes a robust CORS setup:

Happy coding!

Top comments (0)