Back to BlogWeb Development

Why Your API Calls Fail After Deploying to Production – A CORS & Environment Config Fix

Learn how to diagnose and resolve CORS errors and mis‑configured environment variables that break API calls after a web app goes live.

O
One MegaByte Team
January 5, 2026
6 min read

Introduction

When a web application works perfectly in development but starts throwing CORS errors or 404 responses after it’s deployed, the problem is usually a combination of cross‑origin resource sharing (CORS) mis‑configurations and environment variable mismatches. These issues can cripple user experience, increase support tickets, and delay releases.

In this article, we’ll walk through a systematic, step‑by‑step approach to identify the root cause, fix server‑side CORS settings, correctly manage environment variables, and verify the solution with practical code examples. By the end, you’ll have a repeatable checklist that can be applied to any modern JavaScript front‑end (React, Vue, Angular, Svelte) consuming a REST or GraphQL API.


1. The Symptom: API Calls Working Locally, Failing in Production

Typical error messages you’ll see in the browser console:

Access to fetch at 'https://api.example.com/users' from origin 'https://app.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Or:

GET https://api.example.com/users 404 (Not Found)

Both errors point to different layers:

  • CORS – the browser refuses to expose the response because the server didn’t explicitly allow the requesting origin.
  • 404 – the request is hitting the wrong endpoint, often because an environment variable (e.g., API_URL) still points to a development URL or a missing trailing slash.

2. Diagnose the Problem

2.1 Verify the Request URL

Open the Network tab in Chrome DevTools and copy the full request URL. Then:

curl -I "https://api.example.com/users"

If you receive a 200 OK with the expected headers, the endpoint itself is healthy. If you get 404 or 401, the problem is on the server side.

2.2 Check Response Headers for CORS

Look for the Access-Control-Allow-Origin header in the response. A correct header looks like:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization

If the header is missing or set to * (which is insecure for authenticated APIs), the browser will block the response.


3. Fixing CORS on the Server

The exact implementation depends on your backend stack. Below are three common examples.

3.1 Node.js / Express

Add the cors middleware and configure it to whitelist your production domain:

// server.js
const express = require('express');
const cors = require('cors');
const app = express();

const whitelist = ['https://app.example.com'];
const corsOptions = {
  origin: function (origin, callback) {
    if (!origin || whitelist.indexOf(origin) !== -1) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true, // needed if you send cookies or Authorization header
};

app.use(cors(corsOptions));

// your routes …
app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }]);
});

app.listen(3000, () => console.log('API listening on port 3000'));

Key points:

  • Use a whitelist rather than * when authentication is involved.
  • Enable credentials: true if you rely on cookies or Authorization headers.

3.2 Python / Flask

from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)
# Only allow the production front‑end
CORS(app, resources={r"/*": {"origins": "https://app.example.com"}}, supports_credentials=True)

@app.route('/users')
def users():
    return jsonify([{"id": 1, "name": "Bob"}])

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

3.3 Nginx Reverse Proxy (Common for Micro‑services)

If your API sits behind Nginx, add the headers directly:

server {
    listen 443 ssl;
    server_name api.example.com;

    location / {
        proxy_pass http://backend_upstream;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        add_header Access-Control-Allow-Origin "https://app.example.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
        add_header Access-Control-Allow-Credentials "true" always;
        if ($request_method = 'OPTIONS') {
            return 204;
        }
    }
}

Tip: The always flag ensures the header is added even on error responses, which helps debugging.


4. Aligning Environment Variables Across Environments

4.1 Centralize Config with .env Files

Store the API base URL in a .env file for each environment:

# .env.development
REACT_APP_API_URL=http://localhost:3000

# .env.production
REACT_APP_API_URL=https://api.example.com

In a React app, you can access it via process.env.REACT_APP_API_URL.

4.2 Prevent Accidental Commits

Add .env* to .gitignore and use a CI/CD secret manager (GitHub Actions secrets, GitLab CI variables, Azure Key Vault) to inject the correct value during the build.

4.3 Verify at Runtime

Add a small sanity check in your front‑end bootstrap code:

// src/bootstrap.js
const apiUrl = process.env.REACT_APP_API_URL;
if (!apiUrl) {
  throw new Error('REACT_APP_API_URL is not defined – check your environment variables');
}
console.log('API base URL:', apiUrl);

If the variable is missing, the build will fail early, preventing a broken deployment.


5. Updating the Front‑End to Use the Correct URL

Replace hard‑coded URLs with the environment variable:

// src/api/client.js
import axios from 'axios';

const api = axios.create({
  baseURL: process.env.REACT_APP_API_URL,
  withCredentials: true, // required for cookies / auth headers
});

export const fetchUsers = () => api.get('/users');

Why withCredentials: true? When the API uses session cookies or expects an Authorization header, the browser will only send those credentials if this flag is set and the server’s CORS response includes Access-Control-Allow-Credentials: true.


6. Testing the Fix in a Staging Environment

  1. Deploy the updated backend with the new CORS configuration to a staging sub‑domain (e.g., staging-api.example.com).
  2. Deploy the front‑end pointing to that staging API (REACT_APP_API_URL=https://staging-api.example.com).
  3. Open the site in an incognito window and:
    • Verify the Network tab shows a 200 response with the correct CORS headers.
    • Run the console command fetchUsers().then(r => console.log(r.data)) to confirm data is returned.
  4. Once verified, promote the same artifacts to production – no code changes required.

7. Checklist – Prevent Future Breakages

  • CORS whitelist includes every domain that will serve the front‑end (including sub‑domains like www).
  • Access-Control-Allow-Credentials is set only when you truly need it; otherwise omit to avoid security risks.
  • Environment variables are injected via CI/CD secrets, not hard‑coded.
  • Front‑end uses the variable for all API calls; no leftover absolute URLs.
  • Automated tests include an integration test that hits a mock API with CORS headers to catch regressions.
  • Monitoring – enable logging on the API gateway for blocked CORS requests (e.g., CloudWatch metric filters).

8. Conclusion

CORS errors and mis‑configured environment variables are among the most common reasons why a web app that works locally suddenly fails in production. By systematically diagnosing the request URL, validating response headers, configuring the server to explicitly allow your production origin, and managing API endpoints through secure environment variables, you eliminate the guesswork and create a robust deployment pipeline.

Implement the steps and checklist above, and you’ll turn a frustrating “works on my machine” scenario into a repeatable, confidence‑boosting process for every release.


Keywords: CORS, production deployment, environment variables, API integration, web development troubleshooting, React, Express, Flask, Nginx

Web DevelopmentProblem SolvingTutorial