Next.js + Django on a Single Origin, No CORS Headaches
The moment you run a Next.js frontend and a Django backend as separate services, you run into CORS (Cross-Origin Resource Sharing) issues. A frontend on localhost:3000 calling an API on localhost:8000 looks like a different origin to the browser, which means extra header configuration. In production this gets worse: CORS settings, cookie SameSite behavior, and managing two separate domains or subdomains.
The fix: single origin via nginx
On an airport transfer site I built, I solved this at the root by making nginx the single entry point — serving both Next.js and Django from the same origin.
server {
listen 80;
location /api/ {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
}
location /admin-panel-path/ {
proxy_pass http://backend:8000;
}
location / {
proxy_pass http://frontend:3000;
}
}From the browser's perspective there's just one domain: anything starting with /api goes to Django, the admin path goes to the Django admin panel, and everything else goes to Next.js. No CORS headers needed, because the browser sees everything coming from the same origin.
Hiding the admin path
Using Django admin's default /admin/ path makes it the first target for bot brute-force attempts. Instead, I use an unguessable path:
# config/urls.py
urlpatterns = [
path("unguessable-admin-path/", admin.site.urls),
# ...
]This isn't a real security layer, but it's a cheap, effective filter against the bulk of automated scanning bots.
Bringing it together with Docker Compose
The three services (frontend, backend, nginx) are defined in docker-compose.yml; nginx exposes the only external port, the other two only talk on the internal network. For a full production setup with a database and secrets management on top of this, see a production Docker Compose setup for Next.js + Django:
services:
frontend:
build: ./frontend
expose:
- "3000"
backend:
build: ./backend
expose:
- "8000"
nginx:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- frontend
- backendResult
This approach eliminates CORS configuration, cookie domain issues, and the need to manage two separate SSL certificates. The same architecture works in both development and production, which significantly cuts down on "it worked locally" surprises. If you're looking for a server to run this on, deploying Django on AWS Lightsail walks through setting up nginx and HTTPS from a blank server.