If you've ever found yourself staring at the dreaded "Access to fetch at... has been blocked by CORS policy" error message while developing a Next.js application, you're not alone. As one frustrated developer put it on Reddit, they've "ran into 9 million bugs regarding CORS" while trying to create APIs.
CORS (Cross-Origin Resource Sharing) errors are among the most common and frustrating issues developers face, especially during rapid development phases. While CORS is essential for web security, it can feel like an unnecessary roadblock when you're trying to get your application up and running.
In this comprehensive guide, we'll dive deep into understanding and solving common CORS errors in Next.js 15, with practical solutions that you can implement right away.
Understanding CORS: Why It Exists and Why It's Important
Before we jump into solutions, it's crucial to understand what CORS is and why it matters. CORS is a security mechanism built into web browsers that restricts web applications from making requests to a different domain than the one serving the web page.
For example, if your frontend is running on http://localhost:3000
and it tries to make an API request to https://api.payment/session
, the browser will block this request unless the server explicitly allows it through CORS headers.
This security measure exists to protect users from malicious websites that might try to access sensitive information from other domains without permission. As developers note, "CORS won't let you make requests from a browser to a domain that differs from the origination domain" - and for good reason!
Common CORS Headers You Need to Know
Before diving into specific errors, let's understand the essential CORS headers that control cross-origin access:
Access-Control-Allow-Origin
: Specifies which origins can access the resourceExample:
Access-Control-Allow-Origin: *
(allows all origins)Example:
Access-Control-Allow-Origin: https://yourdomain.com
(allows specific origin)
Access-Control-Allow-Methods
: Defines allowed HTTP methodsExample:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers
: Lists allowed request headersExample:
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials
: Indicates if requests can include credentialsExample:
Access-Control-Allow-Credentials: true
Common CORS Errors and Their Solutions
1. Missing Access-Control-Allow-Origin Header
This is perhaps the most common CORS error you'll encounter in Next.js:
Access to fetch at 'https://api.payment/session' from origin 'http://localhost:3000'
has been blocked by CORS policy: Response to preflight request doesn't pass access
control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Solution:
Add the necessary CORS headers in your Next.js API routes. Here's how to do it:
// pages/api/your-endpoint.js
export default function handler(req, res) {
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Handle the actual request
if (req.method === 'OPTIONS') {
res.status(200).end();
return;
}
// Your API logic here
}
2. Preflight Request Issues
Many developers report issues with preflight requests failing. This happens when the browser sends an OPTIONS request before the actual request, and the server doesn't handle it correctly.
Solution:
Create a middleware to handle CORS consistently across all your API routes:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Add CORS headers
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return response;
}
export const config = {
matcher: '/api/:path*',
};
3. Credentials Issues with Wildcard Origin
A common pitfall occurs when trying to use credentials with a wildcard origin. The browser won't allow requests with credentials when Access-Control-Allow-Origin
is set to *
.
Solution:
Specify the exact origin instead of using a wildcard:
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/api/:path*",
headers: [
{
key: "Access-Control-Allow-Origin",
value: process.env.ALLOWED_ORIGIN || "http://localhost:3000",
},
{
key: "Access-Control-Allow-Credentials",
value: "true",
},
{
key: "Access-Control-Allow-Methods",
value: "GET,OPTIONS,PATCH,DELETE,POST,PUT",
},
],
},
];
},
};
4. CORS Issues with External APIs
Many developers struggle with CORS when trying to access external APIs from their Next.js applications. This is particularly common during local development.
Solution:
Use API routes as a proxy to make requests to external APIs:
// pages/api/proxy/[...path].js
export default async function handler(req, res) {
const { path } = req.query;
const apiUrl = `https://external-api.com/${path.join('/')}`;
try {
const response = await fetch(apiUrl, {
method: req.method,
headers: {
'Content-Type': 'application/json',
// Forward any necessary headers
...req.headers,
},
body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined,
});
const data = await response.json();
res.status(response.status).json(data);
} catch (error) {
res.status(500).json({ error: 'Error proxying request' });
}
}
5. Testing and Debugging CORS Issues
One frustration developers often face is that headers work in Postman but not in the browser. This is because Postman doesn't enforce CORS policies like browsers do.
Debugging Tips:
Use the browser's developer tools Network tab to inspect the actual requests and responses
Check if preflight OPTIONS requests are being handled correctly
Verify all required CORS headers are present in the response
Test with a simple curl command to isolate browser-specific issues:
curl -X OPTIONS -H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
https://your-api.com/endpoint -v
Best Practices and Security Considerations
When implementing CORS in your Next.js application, keep these best practices in mind:
Never Use Wildcards in Production Instead of using
Access-Control-Allow-Origin: *
, explicitly list allowed origins:const allowedOrigins = ['https://app.yourdomain.com', 'https://admin.yourdomain.com']; const origin = req.headers.origin; if (allowedOrigins.includes(origin)) { res.setHeader('Access-Control-Allow-Origin', origin); }
Implement Proper Error Handling Always return appropriate error responses when CORS checks fail:
if (!allowedOrigins.includes(origin)) { return res.status(403).json({ error: 'Origin not allowed' }); }
Use Environment Variables Store allowed origins in environment variables for different environments:
const allowedOrigins = process.env.ALLOWED_ORIGINS.split(',');
Regular Auditing Regularly review and update your CORS policies to ensure they align with your security requirements.
Conclusion
CORS errors in Next.js can be frustrating, but they're not insurmountable. By understanding why CORS exists and implementing the appropriate solutions, you can effectively manage cross-origin requests while maintaining security.
Remember that while it might be tempting to use broad CORS permissions during development, it's crucial to implement proper restrictions in production. As one developer wisely noted, "The annoying part is local development, in a rush" - but taking the time to implement CORS correctly will save you from security headaches down the road.
By following the solutions and best practices outlined in this guide, you'll be well-equipped to handle CORS issues in your Next.js applications effectively and securely.