Setting Up Subdomains for Frontend and Backend api and app Separation
Learn how to set up subdomains to separate your frontend and backend using api.yourdomain.com for the API and app.yourdomain.com for the React app.
Setting Up Subdomains for Frontend and Backend
Separating your frontend and backend on different subdomains (e.g., api.yourdomain.com and app.yourdomain.com) is a common production pattern.
Why Use Separate Subdomains?
- Clean separation API and frontend are independent
- Scalable Each can be deployed/scaled separately
- Security Different CORS and security policies
- CDN-friendly Frontend on a CDN, API on EC2
- Cookie scoping Cookies can be scoped per subdomain
Subdomain Structure
app.yourdomain.com → Frontend (React app on Vercel or Nginx)
api.yourdomain.com → Backend (Node.js on EC2)
yourdomain.com → Redirects to app.yourdomain.com
Step 1: Create DNS Records
A Record for API:
Type: A
Name: api
Value: 12.34.56.78 (EC2 Elastic IP)
CNAME for App (if on Vercel):
Type: CNAME
Name: app
Value: cname.vercel-dns.com
A Record for App (if on EC2):
Type: A
Name: app
Value: 12.34.56.78 (same EC2)
Step 2: Configure Nginx Server Blocks
# API server block server { listen 80; server_name api.yourdomain.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } # Frontend server block (if serving from EC2) server { listen 80; server_name app.yourdomain.com yourdomain.com www.yourdomain.com; root /home/ubuntu/devtinder-frontend/dist; location / { try_files $uri $uri/ /index.html; } # Static file caching location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } } # Redirect root to app server { listen 80; server_name yourdomain.com; return 301 http://app.yourdomain.com$request_uri; }
sudo nginx -t && sudo systemctl reload nginx
Step 3: Add SSL for All Subdomains
sudo certbot --nginx -d api.yourdomain.com sudo certbot --nginx -d app.yourdomain.com sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Or all at once:
sudo certbot --nginx -d api.yourdomain.com -d app.yourdomain.com -d yourdomain.com -d www.yourdomain.com
Step 4: Update Backend CORS
// app.js app.use(cors({ origin: [ "https://app.yourdomain.com", "https://yourdomain.com", "http://localhost:5173" // For development ], credentials: true }));
Update .env:
CLIENT_URL=https://app.yourdomain.com
Step 5: Update Frontend Environment
VITE_API_URL=https://api.yourdomain.com/api VITE_SOCKET_URL=https://api.yourdomain.com
Step 6: Cookie Configuration for Cross-Subdomain
Since frontend (app.yourdomain.com) and backend (api.yourdomain.com) are on different subdomains, cookies need special configuration:
res.cookie("token", token, { httpOnly: true, secure: true, sameSite: "none", // Allow cross-site cookies domain: ".yourdomain.com", // Share across subdomains maxAge: 7 * 24 * 60 * 60 * 1000 });
Key settings:
sameSite: "none"Allows cookies to be sent cross-site (requiressecure: true)domain: ".yourdomain.com"Cookie is available on all subdomainssecure: trueHTTPS only (required with sameSite: "none")
Step 7: Deploy Frontend to Vercel (Alternative)
If using Vercel for the frontend:
- Go to Vercel → Your Project → Settings → Domains
- Add
app.yourdomain.com - Vercel provides a CNAME to add to your DNS
- Add the CNAME in your DNS provider
- SSL is automatic on Vercel
Step 8: Verify
# Check API curl https://api.yourdomain.com/api/health # Check Frontend curl -I https://app.yourdomain.com # Check root redirect curl -I https://yourdomain.com # Should return 301 to app.yourdomain.com
The Takeaway
Setting up subdomains involves: creating DNS A records for api and app subdomains (or CNAME for Vercel), configuring Nginx server blocks with matching server_name, adding SSL with certbot for each subdomain, updating CORS to allow the frontend origin, and configuring cookies with sameSite: "none", secure: true, and domain: ".yourdomain.com" for cross-subdomain auth.
Clean separation of concerns, independent deployment and scaling, different security and CORS policies, CDN-friendly frontend, and cookie scoping per subdomain. Common setup: api.yourdomain.com for backend, app.yourdomain.com for frontend.
Set sameSite: 'none' (allows cross-site cookies), secure: true (required with sameSite none, HTTPS only), domain: '.yourdomain.com' (shares cookie across all subdomains), and httpOnly: true. This lets the frontend on app.yourdomain.com send cookies to the backend on api.yourdomain.com.
Create separate server blocks for each subdomain: one with server_name api.yourdomain.com and proxy_pass to Node.js, another with server_name app.yourdomain.com and root pointing to the frontend build. Add a redirect from yourdomain.com to app.yourdomain.com.
Run sudo certbot --nginx -d api.yourdomain.com -d app.yourdomain.com -d yourdomain.com -d www.yourdomain.com to get a single SAN certificate for all domains. Or run certbot separately for each subdomain to get individual certificates.
In Vercel project settings, add app.yourdomain.com as a domain. Vercel provides a CNAME record. Add that CNAME in your DNS provider (Name: app, Value: cname.vercel-dns.com). SSL is automatic on Vercel.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

