
- Next.js
- Nginx
- Deployment
- SSL
- Node.js
Deploying Next.js with Nginx
A comprehensive guide to deploying your Next.js application on a server running Nginx, covering reverse proxy setup, gzip compression, static caching, and SSL configuration.
This guide covers deploying a Next.js application on a server running Nginx. Two modes are covered: a standard Node.js deployment with full SSR support, and a static export deployment where Nginx serves flat HTML files directly.
Standard Deployment with Node.js
1. Build your Next.js app
Build locally before transferring to the server:
# Navigate to your project directory
cd your-next-app
# Install dependencies if you haven't already
npm install
# Build the app
npm run build
# This creates a .next directory with the built app
2. Transfer files to your server
# Example using SCP (run from your local machine)
scp -r .next package.json package-lock.json public next.config.js username@your-server-ip:/path/to/your/app
3. Install dependencies on your server
SSH into your server and install production dependencies:
cd /path/to/your/app
npm install --production
4. Set up Nginx as a reverse proxy
Install Nginx if you haven’t already:
sudo apt update
sudo apt install nginx
Create a new Nginx configuration file for your site:
sudo nano /etc/nginx/sites-available/nextjs
Add this configuration (adjust domain names and paths as needed):
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Enable gzip compression
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
application/javascript
application/json
application/x-javascript
application/xml
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
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;
proxy_cache_bypass $http_upgrade;
}
# Long-term caching for static assets
location /_next/static/ {
proxy_pass http://localhost:3000/_next/static/;
expires 365d;
access_log off;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location /public/ {
proxy_pass http://localhost:3000/public/;
expires 365d;
access_log off;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}
Enable the site, test the configuration, and restart Nginx:
sudo ln -s /etc/nginx/sites-available/nextjs /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
5. Run your Next.js server with PM2
PM2 manages the Node.js process and restarts it on server reboot. For a full PM2 reference covering ecosystem config files, cluster mode, and log rotation, see the PM2 Node.js Process Management Guide.
# Install PM2 globally
sudo npm install -g pm2
# Start your Next.js app
cd /path/to/your/app
pm2 start npm --name "next-app" -- start
# Persist across reboots
pm2 startup
pm2 save
6. Set up SSL with Certbot
Install Certbot and obtain a Let’s Encrypt certificate. For a complete SSL guide covering HSTS, auto-renewal, and troubleshooting, see Set Up HTTPS with Let’s Encrypt on Nginx and Apache.
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Obtain and install certificates
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Verify auto-renewal is configured
sudo systemctl status certbot.timer
Certbot will automatically modify your Nginx configuration to handle SSL.
Static Export Deployment
Use this approach when you don’t need SSR, API routes, or middleware.
1. Configure your app for static export
Add the output setting to your next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
}
module.exports = nextConfig
2. Build your app
npm run build
This creates an out directory with static files ready to serve.
3. Install Nginx
sudo apt update
sudo apt install nginx
4. Transfer the built files to your server
# From your local machine
scp -r ./out/* username@your-server-ip:/var/www/yourdomain.com/html/
Create the directory and set permissions on the server first:
sudo mkdir -p /var/www/yourdomain.com/html
sudo chown -R $USER:$USER /var/www/yourdomain.com/html
sudo chmod -R 755 /var/www/yourdomain.com
5. Configure the Nginx server block
sudo nano /etc/nginx/sites-available/yourdomain.com
server {
listen 80;
listen [::]:80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/yourdomain.com/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
application/javascript
application/json
application/x-javascript
application/xml
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
# Long-term caching for static assets
location /_next/static/ {
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location / {
try_files $uri $uri.html $uri/ =404;
}
# Custom 404 page
error_page 404 /404.html;
}
6. Enable the site and restart Nginx
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
7. Set up SSL
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Important Notes
- Your server must have Node.js installed. Check the official Next.js docs for the minimum Node.js version required by your release — each major version may raise the minimum.
- Static export does not support SSR, API routes, server actions, or middleware.
- Configure proper file permissions so Nginx can read your static files.
- Always use HTTPS in production and set up logging and monitoring for production deployments.
Frequently Asked Questions
Is Nginx better than Apache for Next.js?
Nginx is often preferred for Next.js deployments due to its efficient handling of static assets, lower memory footprint, and better performance under high load. It excels at serving static content and acting as a reverse proxy. If you prefer Apache, see the Deploying Next.js with Apache2 guide for an equivalent setup.
How do I handle client-side routing with static export?
Add a location block in your Nginx config to redirect requests to the appropriate HTML file, since Next.js client-side routing expects the server to serve the main index.html for all routes.
How do I manage environment variables?
For SSR deployments, set environment variables directly on your server. For static exports, variables consumed client-side must be prefixed with NEXT_PUBLIC_ and available at build time — they get embedded into the JavaScript bundles.
How do I update the deployment after code changes?
For standard deployments: pull the latest code, rebuild, and restart the PM2 process. For static exports: rebuild locally, copy the new out directory to your server — no Nginx restart needed.