At GoGrow, we recently needed to host multiple Next.js applications under a single domain, each served from a different path (e.g., app.example.com/app1, app.example.com/app2, etc.).
The goal was to keep everything under one consistent domain for easier authentication, cookie handling, and SSL management.
In this post, we’ll share how we set this up using AWS Amplify and NGINX, and how we handled the key challenges like authentication (NextAuth), static assets, and Google OAuth flows.
The Objective
We wanted:
- A single root domain (app.example.com)
- Multiple Next.js apps under different paths, such as:
- /app1 → App 1 (deployed via AWS Amplify)
- /app2 → App 2 (future app or similar setup)
- Proper proxy routing and SSL through NGINX
- Working authentication and Google login (via NextAuth)
- Seamless local and production parity (no need to change app logic)
Why Sub-Paths Instead of Subdomains?
You might ask, why not just use app1.example.com, app2.example.com, etc.?
We opted for sub-path routing instead, for a few key reasons:
- Simpler routing through a single NGINX reverse proxy.
- Unified cookie and session handling, avoiding SameSite and cross-domain issues.
- Centralized SSL with a single certificate managed via Certbot.
- Cleaner domain structure and less DNS management.
Step 1: Updating the Next.js App Configuration
Each app needs to know it’s being served under a base path.
In next.config.js, add:
module.exports = {
basePath: '/app1',
assetPrefix: '/app1',
};- basePath ensures that all routes are prefixed correctly.
- assetPrefix ensures static assets load from /app1 as well.
Step 2: Configuring NextAuth
To make authentication work under this setup, we updated environment variables and middleware:
# Production
NEXTAUTH_URL=https://app.example.com/app1/api/auth
# Development
NEXTAUTH_URL=http://localhost:3000/app1/api/authIn your app:
<SessionProvider basePath="/app1/api/auth">Also, make sure your API routes and redirects use the same /app1 prefix, so users are correctly redirected after login.
Step 3: Deploying to AWS Amplify
Each app is deployed independently on AWS Amplify under its own custom subdomain (e.g. app1.example.com).
Amplify handles build/deploy, while NGINX later proxies requests from the main domain.
In Amplify:
- Connect your repo and set up the custom domain app1.example.com.
- Keep the default Amplify rewrites and redirects, NGINX will handle the path rewriting.
Step 4: NGINX Setup
On our EC2 instance hosting app.example.com, we use NGINX to:
- Manage SSL (via Certbot)
- Serve a static landing page
- Proxy requests for /app1 to the Amplify app
DNS
Point app.example.com to your EC2 server’s public IP.
SSL Setup
sudo certbot certonly --nginx -d app.example.com
sudo certbot renew --dry-runNGINX Configuration
Edit /etc/nginx/sites-available/app.example.com to include the correct proxy configuration for /app1
server {
server_name app.example.com;
root /var/www/html;
index index.html;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
try_files $uri $uri/ =404;
}
location /app1 {
proxy_pass https://app1.example.com;
proxy_ssl_server_name on;
proxy_set_header Host app1.example.com;
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;
proxy_set_header Referer "https://app1.example.com";
proxy_set_header Origin "https://app1.example.com";
proxy_redirect https://app1.example.com https://app.example.com/app1;
}
}
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}Enable and restart:
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginxReplace NGINX’s default page with a basic HTML index:
<!DOCTYPE >
<html>
<head><meta charset="UTF-8"><title>Welcome to Example App</title></head>
<body>
<h1>Welcome to Example App</h1>
<ul>
<li><a href="/app1">App 1</a></li>
<li><a href="/app2">App 2 (coming soon)</a></li>
</ul>
</body>
</html>This provides a simple user-friendly homepage at https://app.example.com.
Restart NGINX
sudo nginx -t
sudo systemctl restart nginxStep 5: Google OAuth Configuration
Update your Google Cloud Console OAuth settings to ensure Google login works correctly with the custom paths and domains:
Authorized JavaScript Origins:
http://localhost:3000
https://app1.example.com
https://app.example.comAuthorized Redirect URIs:
http://localhost:3000/app1/api/auth/callback/google
https://app.example.com/app1/api/auth/callback/google
https://app1.example.com/app1/api/auth/callback/googleMake sure /app1/api/auth/callback/google is included - it’s critical for the OAuth flow to complete.
Testing
Local
- Run the app at http://localhost:3000/app1
- Test Google login and routes
Production
- Visit https://app.example.com/app1
- Verify routing, static assets, and NextAuth flows
Summary
With this setup, we can:
- Host multiple Next.js apps under one domain
- Use NGINX as a reverse proxy to route paths cleanly
- Keep authentication consistent with NextAuth
- Simplify SSL and DNS management
- Deploy and scale each app independently via Amplify
This approach gives us flexibility to add new apps easily under the same domain structure - all while keeping authentication, cookies, and security consistent.
