Deploying a Nuxt 3 application with Server-Side Rendering (SSR) on Laravel Forge requires specific configurations to ensure that Nuxt is properly served. This guide provides a step-by-step walkthrough to deploy your Nuxt 3 SSR app on Forge, configure Nginx, and manage Nuxt processes using PM2.
Step 1: Prepare Your Nuxt 3 App for Deployment
Before deploying, ensure your Nuxt app is properly configured for SSR. Update your nuxt.config.js
file:
export default defineNuxtConfig({
ssr: true, // Enable Server-Side Rendering
nitro: {
prerender: {
crawlLinks: false, // Prevent Nitro from crawling and generating routes
routes: [], // No pre-rendered routes
},
},
// ... other config here
})
Why this matters?
- When SSR is enabled, Nuxt does not serve static files from
/dist
. - Instead, the SSR application is served from
.output/public/server
. - This means all requests are handled dynamically by Nuxt.
- We need PM2 to manage the Nuxt server, ensuring it runs continuously.
Step 2: Create a New Static App on Laravel Forge
- Log in to your Laravel Forge account.
- Create a new site and select Static App as the site type.
- Add your domain (e.g.,
yourdomain.com
). - Assign an SSL certificate (Let's Encrypt or manually).
- Once SSL is active, edit the Nginx configuration file.
Editing Nginx Configuration on Forge
Rather than editing the Nginx config directly on the server, follow these steps:
- Navigate to your site on Forge.
- Locate the Edit Files dropdown on the top-right side of the page.
- Select Edit Nginx Configuration.
- Replace the existing configuration with the following, ensuring you replace:
-
YOUR_DOMAIN_NAME
with your actual domain. -
YOUR_DOMAIN_FOLDER
with the correct site directory. - Keep the SSL ID values assigned by Forge for your certificate.
-
Example:
# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/YOUR_DOMAIN_FOLDER/before/*;
map $sent_http_content_type $expires {
"text/html" epoch;
"text/html; charset=utf-8" epoch;
default off;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name YOUR_DOMAIN_NAME;
# FORGE SSL (DO NOT REMOVE!)
ssl_certificate /etc/nginx/ssl/YOUR_DOMAIN_NAME/YOUR_SSL_ID/server.crt;
ssl_certificate_key /etc/nginx/ssl/YOUR_DOMAIN_NAME/YOUR_SSL_ID/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_dhparam /etc/nginx/dhparams.pem;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
charset utf-8;
gzip on;
gzip_types text/plain application/xml text/css application/javascript;
gzip_min_length 1000;
# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/YOUR_DOMAIN_FOLDER/server/*;
location / {
expires $expires;
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_redirect off;
proxy_read_timeout 1m;
proxy_connect_timeout 1m;
proxy_pass http://127.0.0.1:3000; # Set the address of the Node.js server
}
access_log off;
error_log /var/log/nginx/YOUR_DOMAIN_NAME-error.log error;
location ~ /\.(?!well-known).* {
deny all;
}
}
# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/YOUR_DOMAIN_FOLDER/after/*;
After saving changes in Forge, restart Nginx by clicking Apply Changes.
Step 3: Set Up Deployment Script on Forge
Navigate to your site on Laravel Forge and go to the Deployment tab. Update the deployment script with the following:
#!/bin/bash
cd /home/forge/YOUR_DOMAIN_NAME
# Pull latest changes
git pull origin $FORGE_SITE_BRANCH
# Clean installation files
rm -rf node_modules
rm package-lock.json
# Install dependencies with platform-specific flags
export ROLLUP_SKIP_NODE_CHECK=true
npm install --platform=linux --arch=x64
# Build for SSR
npm run build
# Restart Nuxt using PM2
pm2 delete nuxt-app || true # Ensure old process is removed
pm2 start .output/server/index.mjs --name "nuxt-app"
# Save PM2 process so it persists after a reboot
pm2 save
# Restart PHP-FPM (if needed for other Laravel services)
( flock -w 10 9 || exit 1
echo 'Restarting FPM...'; sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock
Click Save and then Deploy Now to start the process.
Step 4: Finalizing Your Deployment
- Ensure that your Web Directory in Laravel Forge is set to:
/public
- Manually restart all services to ensure everything is working:
sudo service nginx restart
pm2 restart nuxt-app
Open your domain in the browser (
https://yourdomain.com
) and check that the app is loading correctly.Monitor logs if there are any issues:
tail -f /var/log/nginx/YOUR_DOMAIN_NAME-error.log
pm2 logs nuxt-app
Conclusion
By following these steps, you have successfully deployed a Nuxt 3 SSR application on Laravel Forge. This setup ensures:
- Nuxt 3 runs in server-side rendering mode.
- Nginx properly proxies requests to the Nuxt SSR server.
- PM2 keeps the Nuxt process running reliably.
This guide should serve as a complete reference for anyone deploying Nuxt 3 SSR on Laravel Forge. 🚀