CORS

CORS in Docker — Why localhost Works but Container Does Not

When you run your frontend and backend in separate Docker containers, they cannot reach each other via localhost. Container networking has its own namespace — localhost inside a container refers to the container itself, not your host machine.

The Docker networking problem

# This works locally (no Docker):
fetch('http://localhost:8000/api')  # frontend on :3000 → backend on :8000

# This breaks in Docker:
# frontend container: localhost = the frontend container
# backend container: localhost = the backend container
# They are different machines

Fix 1 — Use service names in docker-compose

Docker Compose creates a network where containers can reach each other by their service name. Use the service name instead of localhost:

# docker-compose.yml
services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    environment:
      - REACT_APP_API_URL=http://backend:8000  # use service name

  backend:
    build: ./backend
    ports:
      - "8000:8000"
    environment:
      - CORS_ORIGIN=http://localhost:3000  # browser still uses localhost

The browser calls the backend using http://localhost:8000 (the host port mapping). The backend must allow the origin http://localhost:3000 for CORS.

Fix 2 — For host machine access, use host.docker.internal

# From inside a container, reach the host machine:
fetch('http://host.docker.internal:8000/api')

# Or in docker-compose:
extra_hosts:
  - "host.docker.internal:host-gateway"

Fix 3 — Nginx reverse proxy for both services

The cleanest setup: Nginx in front of both. Same origin = no CORS needed:

# nginx.conf inside the Nginx container
server {
    listen 80;

    location / {
        proxy_pass http://frontend:3000;
    }

    location /api/ {
        proxy_pass http://backend:8000/;
        # No CORS needed — same origin from browser's perspective
    }
}

CORS config for Docker environment

// Express backend — allow the host port the browser uses
app.use(cors({
  origin: [
    'http://localhost:3000',     // browser access via host port mapping
    'http://frontend:3000',      // container-to-container (if needed)
  ],
  credentials: true,
}));
Test your Docker CORS config →