Problem
Next.js app in Docker container works fine in development but faces CORS issues in production when calling an external API (https://api.temp.com).
Environment
- Next.js version: 13.2.4
- Docker version: 4.33.0
- Docker Compose version: 3
- Operating System: Mac
Error Message
Access to XMLHttpRequest at 'https://api.temp.com/v1/popup/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Dockerfile (Production)
FROM node:20-alpine AS base
# Step 1. Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
# Omit --production flag for TypeScript devDependencies
RUN
if [ -f yarn.lock ]; then yarn --frozen-lockfile;
elif [ -f package-lock.json ]; then npm ci;
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i;
# Allow install without lockfile, so example works even without Node.js installed locally
else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install;
fi
COPY src ./src
COPY public ./public
COPY next.config.js .
COPY tsconfig.json .
COPY holiday-kr.d.ts .
# Environment variables must be present at build time
# https://github.com/vercel/next.js/discussions/14030
ARG NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_SOCKET_URL
ARG NEXT_PUBLIC_APP_NAME
ARG NEXT_PUBLIC_DOMAIN
ARG NEXT_PUBLIC_GOOGLE_CLIENT_ID
ARG NEXT_PUBLIC_KAKAO_CLIENT_ID
ARG NEXT_PUBLIC_FLARELANE_PROJECT_ID
ARG NEXT_PUBLIC_FLARELANE_API_KEY
ENV NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}
NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL}
NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}
NEXT_PUBLIC_DOMAIN=${NEXT_PUBLIC_DOMAIN}
NEXT_PUBLIC_GOOGLE_CLIENT_ID=${NEXT_PUBLIC_GOOGLE_CLIENT_ID}
NEXT_PUBLIC_KAKAO_CLIENT_ID=${NEXT_PUBLIC_KAKAO_CLIENT_ID}
NEXT_PUBLIC_FLARELANE_PROJECT_ID=${NEXT_PUBLIC_FLARELANE_PROJECT_ID}
NEXT_PUBLIC_FLARELANE_API_KEY=${NEXT_PUBLIC_FLARELANE_API_KEY}
# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry
# Uncomment the following line to disable telemetry at build time
# ENV NEXT_TELEMETRY_DISABLED 1
# Build Next.js based on the preferred package manager
RUN
if [ -f yarn.lock ]; then yarn build;
elif [ -f package-lock.json ]; then npm run build;
elif [ -f pnpm-lock.yaml ]; then pnpm build;
else npm run build;
fi
# Note: It is not necessary to add an intermediate step that does a full copy of `node_modules` here
# Step 2. Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
# Don't run production as root
RUN addgroup --system --gid 1001 nodejs &&
adduser --system --uid 1001 nextjs
USER nextjs
COPY --from=builder /app/public ./public
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Environment variables must be redefined at run time
ARG NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_SOCKET_URL
ARG NEXT_PUBLIC_APP_NAME
ARG NEXT_PUBLIC_DOMAIN
ARG NEXT_PUBLIC_GOOGLE_CLIENT_ID
ARG NEXT_PUBLIC_KAKAO_CLIENT_ID
ARG NEXT_PUBLIC_FLARELANE_PROJECT_ID
ARG NEXT_PUBLIC_FLARELANE_API_KEY
ENV NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}
NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL}
NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}
NEXT_PUBLIC_DOMAIN=${NEXT_PUBLIC_DOMAIN}
NEXT_PUBLIC_GOOGLE_CLIENT_ID=${NEXT_PUBLIC_GOOGLE_CLIENT_ID}
NEXT_PUBLIC_KAKAO_CLIENT_ID=${NEXT_PUBLIC_KAKAO_CLIENT_ID}
NEXT_PUBLIC_FLARELANE_PROJECT_ID=${NEXT_PUBLIC_FLARELANE_PROJECT_ID}
NEXT_PUBLIC_FLARELANE_API_KEY=${NEXT_PUBLIC_FLARELANE_API_KEY}
# Uncomment the following line to disable telemetry at run time
# ENV NEXT_TELEMETRY_DISABLED 1
# Note: Don't expose ports here, Compose will handle that for us
CMD ["node", "server.js"]
I don’t know if I should implement next custom server to set crt or add access allow setting to api server.
It works without error in the development environment (http://localhost:3000), but I don’t know why the cors error occurs in the production environment (http://localhost:3000).
Should I:
Modify the API server to include CORS headers, or
Implement a custom Next.js server to handle CORS?
The localhost:3000 already works well in the development environment, but I don’t know which url should be allowed to access
Attempted
Confirmed API endpoint is correct
Verified environment variables in both dev and prod containers
Any guidance on the best solution would be appreciated.