How to deploy NextJS with NGINX? - reactjs

So I know how to deploy a React app on a server.
npm run build
create a server block and point the root to my react app folder build (root /var/www/xfolder/build;)
systemctl restart nginx
run my node server (nohup node server &&) and its done.
I feel kind of dumb for not understanding this with NextJS. I run npm run build
I'm expecting something like a build folder. I've tried setting the server block root to
/var/www/xfolder/.next but the page still gives 403 forbidden. And do I need to run npm run start? I'm confuse on how to properly deploy the app. I'm using Ubuntu, NginX (1gb droplet) in DigitalOcean.

Check this: https://gist.github.com/iam-hussain/2ecdb934a7362e979e3aa5a92b181153
Reference for HTTP/HTTPS: https://gist.github.com/iam-hussain/2ecdb934a7362e979e3aa5a92b181153
Start PM2 nextJS service on port 8080:
cd PROJECT_DIRECTORY
pm2 start "npm run start -- -p 8080" --name contractverifier
Configure Nginx:
Replace this file with the below code /etc/nginx/sites-available/default
server {
server_name www.DOMAINNAME.com DOMAINNAME.com;
index index.html index.htm;
root /home/ubuntu/PROJECT_FOLDER; #Make sure your using the full path
# Serve any static assets with NGINX
location /_next/static {
alias /home/ubuntu/PROJECT_FOLDER/.next/static;
add_header Cache-Control "public, max-age=3600, immutable";
}
location / {
try_files $uri.html $uri/index.html # only serve html files from this dir
#public
#nextjs;
add_header Cache-Control "public, max-age=3600";
}
location #public {
add_header Cache-Control "public, max-age=3600";
}
location #nextjs {
# reverse proxy for next server
proxy_pass http://localhost:8080; #Don't forget to update your port number
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen 80 default_server;
listen [::]:80;
}

I managed to make it work. The problem is on my Nginx server block. I just add this block
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_cache_bypass $http_upgrade;
}
then run
npm start

I prefer to pm2 in order to start nextJs service and Nginx for publishing it
pm2 cmd:
pm2 start yarn --name nextjs --interpreter bash -- start
pm2 show nextjs
You can push that config into /etc/nginx/conf.d/your-file.config
/etc/nginx/nginx.config
server {
listen 80; # you can use 443 and letsencrypt to get SSL for free
server_name dicom-interactive.com; # domain name
access_log /var/log/dicom-interactive/access.log; # mkdir dir first
error_log /var/log/dicom-interactive/error.log error;
# for public asset into _next directory
location _next/ {
alias /srv/udemii-fe/.next/;
expires 30d;
access_log on;
}
location / {
# reverse proxy for next server
proxy_pass http://localhost:8000; # your nextJs service and port
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# we need to remove this 404 handling
# because next's _next folder and own handling
# try_files $uri $uri/ =404;
}
}

Related

How to host multiple create-react-app development servers under nginx with working live (hot) reload

I am developing a website with React.js for the frontend and have 2 separate apps for the users and the admins. The users will be under example.com and the admins under example.com/admin.
I am developing both apps behind an nginx server as a reverse proxy. I have had no issue developing a single app behind nginx, but I cannot use hot reload for the 2nd app. The app is served properly, with the only exception that the hot reload does not work.
I have HTTPS=true on both my .env files of the React.js apps. The main app's hot reload works fine, but the /admin app's hot reload fails with the error Firefox can’t establish a connection to the server at wss://192.168.1.2/adminws (developing through local network, so I can test the apps on my phone as well, but the hot reload won't work on the localhost either).
The main app is hosted under port 3000, the admin app is hosted under port 4000.
This is what my main app's .env looks like:
HTTPS=true
WDS_SOCKET_PORT=443
FAST_REFRESH=true
This is what my admin app's .env looks like:
HTTPS=true
WDS_SOCKET_PORT=443
WDS_SOCKET_PATH=/adminws
FAST_REFRESH=true
This is what my nginx configuration file looks like:
server {
# listen 80 default_server;
# listen [::]:80 default_server;
# SSL configuration
#
listen 443 ssl http2 default_server;
listen [::]:443 ssl default_server;
ssl on;
ssl_certificate /etc/nginx/ssl/localhost.crt;
ssl_certificate_key /etc/nginx/ssl/localhost.key;
gzip on;
gzip_types text/plain application/xml application/json;
gzip_proxied any;
gzip_min_length 1000;
gunzip on;
gzip_static on;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location /ws {
proxy_pass https://127.0.0.1:3000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /adminws {
proxy_pass https://127.0.0.1:4000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api {
proxy_pass http://127.0.0.1:3200;
}
location /admin {
proxy_pass https://127.0.0.1:4000;
}
location / {
proxy_pass https://127.0.0.1:3000;
}
}
I should note that the admin app's hot reload works properly when I remove both WDS_SOCKET_PORT and WDS_SOCKET_PATH from the .env file and run it on https://localhost:4000/admin, but this way I would not be able to test it behind nginx.
I removed both WDS_SOCKET_PORT and WDS_SOCKET_PATH from the admin app's .env file and it now seems to be working properly. Everything else seems to be ok.

How to deploy react Remix framework for production on nginx?

What config file I need to launch Remix application?
It has no index.html file
Steps to reproduce (https://remix.run/docs/en/v1/guides/deployment):
npx create-remix#latest
? Where would you like to create your app? (./my-remix-app)
? Where do you want to deploy? Choose Remix if you're unsure, it's easy to change deployment targets. (Use arrow keys)
❯ Remix App Server
? TypeScript or JavaScript? (Use arrow keys)
❯ TypeScript
cd my-remix-app
npm run build
And we have to directories: public, build
And what is the next step to show it on website.com using nginx?
The easiest is to choose the Remix App Server (which uses Express internally) or Express, then run remix build to build the app for production and run npm start to run the server.
After that, it's a normal Node.js server so you can configure your NGINX to forward requests on port 80 and 443 to your Remix app running in another port (3000 by default). This is normal Node.js + NGINX deployment, nothing specific of Remix.
right, you should build and start the node app first.
here's an example:
server {
listen 80;
listen 443 ssl http2;
server_name example.com;
ssl_certificate /home/example.com/ssl.crt;
ssl_certificate_key /home/example.com/ssl.key;
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";
# add_header Access-Control-Allow-Origin "*";
add_header Strict-Transport-Security "max-age=31536000;";
access_log off;
# error_log /home/logs/error.nginx.log crit;
location / {
if ($http_user_agent = Mozilla/4.0){
return 503;
}
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:3000/;
proxy_redirect off;
}
}

Nginx reverse proxy and multiple React apps

So I'm trying to use NGINX as a reverse proxy for 2 react apps and 1 node js api. Each in separate docker containers.
So for example,
localhost -> leads to one react app
localhost/admin -> leads to another react app
localhost/api/getProducts -> leads to the /getProducts endpoint of the api
The first example and the second both work as intended. No issues. It's the 2nd example I'm having trouble configuring. It should just lead to a dashboard application built in React, but all I get is a white screen (with the same favicon as the first react app).
Here is my nginx config file
upstream api {
least_conn;
server api:8080 max_fails=3 fail_timeout=30s;
}
upstream app {
least_conn;
server app:3000 max_fails=3 fail_timeout=30s;
}
upstream adminapp {
least_conn;
server adminapp:3001 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
if ($request_method = 'OPTIONS') {
return 200;
}
# To allow POST on static pages
error_page 405 =200 $uri;
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
expires 30d;
break;
}
location ~ /admin/(?<url>.*) {
proxy_pass http://adminapp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
expires 30d;
break;
}
location ~ /api/(?<url>.*) {
proxy_pass http://api/$url;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /health-check {
return 200;
access_log off;
}
}
}
When I specifically go to localhost:3001, I can reach the admin dashboard so I know it's running perfectly fine.
Here's my docker compose file as well
version: '3.7'
services:
nginx:
container_name: nginx
image: nginx
ports:
- '80:80'
- '443:443'
links:
- api:api
- app:app
- adminapp:adminapp
volumes:
- ./server/config/nginx:/etc/nginx
- ./server/config/certs:/etc/ssl/private
app:
container_name: app
build:
context: ./frontend
dockerfile: Dockerfile
volumes:
- './frontend:/usr/app/frontend/'
- '/usr/app/frontend/node_modules'
ports:
- '3000:3000'
environment:
- NODE_ENV=development
adminapp:
container_name: adminapp
build:
context: ./admin
dockerfile: Dockerfile
volumes:
- './admin:/usr/app/admin/'
- '/usr/app/admin/node_modules'
ports:
- '3001:3001'
environment:
- NODE_ENV=development
- PORT=3001
api:
container_name: api
build:
context: ./backend
dockerfile: Dockerfile
volumes:
- './backend:/usr/app/backend/'
- '/usr/app/backend/node_modules'
ports:
- '8080'
environment:
- NODE_ENV=development
So this isnt an answer really - because im having the same problem - but if you try setting an PUBLIC_URL as an env variable with "./", or "./your-path", you should see some changes. If you inspect the source of the page and click on the URLs of the static resources you should see the JS there. You may have to modify the path by adding /adminapp before it. I'm going to continue working on it so I'll update with anything else I find. Also FYI this may be relevant:
https://github.com/facebook/create-react-app/issues/8222#issuecomment-568308139
Leaving an answer here because I figured it out. Basically you have to add "homepage": "." to your package.json first. Then, make sure you check your dockerfile. This was my mistake. For it to work on a subdomain you have to explicitly build everything in the Dockerfile as it is here:
FROM node:14-alpine as build-deps
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn
COPY . ./
RUN yarn build
FROM nginx:1.13-alpine
#WORKDIR /usr/src/app
COPY --from=build-deps /usr/src/app/build /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/nginx.conf
EXPOSE 3000
This is how I had it before, and this was incorrect:
# linux distro
FROM node:14-alpine
# workdir inside the container
WORKDIR /usr/src/app
# copy these files to WORKDIR
COPY package.json .
COPY yarn.lock .
# Install all of the npm dependencies on the Docker image.
RUN yarn install
# Copy everything from the folder which this Dockerfile is placed
# and unto the new containers WORKDIR
COPY . .
# Open port 3000
EXPOSE 3000
# Run the script "npm start" defined in package.json
# this will start the dev server and the project will be at the containers ip+ port.
CMD ["yarn", "start"]
If you make sure that you have your Dockerfile correct and you have "homepage", the paths should be correct and your react app should work at the subdomain.
As a side note, if you are having trouble with react routing, check your basepath, that will probably fix it for you. =)
So I realized I never posted how I solved this; and to be honest I forget exactly what I did. I compared the nginx config in my original question to what I have now and realized there were some stark differences. So I'm posting what I have now. I don't think I had to change my docker config or anything. I did switch to the staticfloat/nginx-certbot image for SSL.
What it looks like I did though was set up a separate server block for the subdomain. Not sure if that is the most efficient or whatever, but it ended up working.
server {
listen 80;
server_name domain.com www.domain.com;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443;
server_name admin.domain.com;
ssl_certificate /etc/letsencrypt/live/admin.domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admin.domain.com/privkey.pem;
location / {
proxy_pass http://adminapp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
expires 1d;
break;
}
location ~* \.(eot|otf|ttf|woff|woff2)$ {
expires 365d;
add_header Access-Control-Allow-Origin *;
}
location ~ /api/(?<url>.*) {
proxy_pass http://api/$url;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /health-check {
return 200;
access_log off;
}
}
server {
listen 443 ssl default_server;
# listen 80 default_server;
server_name domain.com;
if ($request_method = 'OPTIONS') {
return 200;
}
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
# To allow POST on static pages
error_page 405 =200 $uri;
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
expires 1d;
break;
}
location ~* \.(eot|otf|ttf|woff|woff2)$ {
expires 365d;
add_header Access-Control-Allow-Origin *;
}
location ~ /api/(?<url>.*) {
proxy_pass http://api/$url;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /health-check {
return 200;
access_log off;
}
}
server {
listen 443 ssl;
# listen 80 default_server;
server_name www.domain.com;
if ($request_method = 'OPTIONS') {
return 200;
}
ssl_certificate /etc/letsencrypt/live/www.domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.domain.com/privkey.pem;
# To allow POST on static pages
error_page 405 =200 $uri;
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
expires 1d;
break;
}
location ~* \.(eot|otf|ttf|woff|woff2)$ {
expires 365d;
add_header Access-Control-Allow-Origin *;
}
location ~ /api/(?<url>.*) {
proxy_pass http://api/$url;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /health-check {
return 200;
access_log off;
}
}

React app doesnt launch from url location as set by nginx reverse proxy but does when port explicitly set

I'm trying to run a single react app on a separate port and using proxy pass from nginx to location '/app'
"http://localhost/app" -> React Application
The application is running under PM2 using its generated build with the command: 'pm2 serve build 3002'.
My thought was i do all this tested locally and then will port it to my own domain.
I've swapped 'localhost' for '127.0.0.1' just in case that had anything to do with it.
From online tutorials i've ended up with the following in my nginx "../sites-available/my_domain" file
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /var/www/my_domain/html;
index index.html index.htm index.nginx-debian.html;
try_files $uri $uri/ =404;
}
location /app {
proxy_pass http://localhost:3002;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
If i type "http://localhost:3002" into the url it works fine however "http://localhost/app" does not work and shows 404 error.
Side-note: "http://localhost" works fine and directs to the index.html stored at root.

NGINX proxy pass to React App not working

I want to run the following projects using NGINX under a single subdomain: http://localhost:3000 (Loopback API) and http://localhost:3006 (React Application)
Both applications are running under PM2. React App is running in production (using its generated build) with the command: 'pm2 serve build 3006'.
/etc/nginx/sites-available/default
server {
listen 80;
server_name subdomain.domain.com;
location / {
proxy_pass http://localhost:3006;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
try_files $uri /index.html;
}
location /api {
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_cache_bypass $http_upgrade;
}
Loopback project under location /api es working perfectly. The issue is with my React project under my / route. When I enter to subdomain.domain.com I just get a blank page. In the developer console I'm getting the following errors:
Using http://localhost:3006 to access my React App works perfectly fine with no console issues so I'm totally sure it is something related with nginx.
I have been investigating a lot about the incorrect MIME type being loaded, but my /etc/nginx/nginx.conf is already running with the following configuration:
http {
include /etc/nginx/mime.types;
}
I would really appreciate your help, thanks.
Are you using Create React App? By default, its build script assumes the app is going to be served in the root directory. To correctly serve React under a subdirectory, you need to specify homepage in your package.json file.
"homepage" : "http://example.com/your-subdirectory"
Additionally, you'll want to modify server/app.js to reflect this change.
app.use('/your-subdirectory/api', require('./api'));
Lastly, and most importantly, you'll want to set a basename for React Router as well.
const Routes = () => {
return (
<Router basename={'/your-subdirectory'}>
<div>
<Route exact path={`/`}component={App} />
</div>
</Router>
)
};
There is no need to use pm2 since they are static files and nginx is able to serve it by itself.
You just have to let nginx know where the files are.
server {
listen 80;
root /home/user/app/buld <--- LOCATION_OF_YOUR_BUILD
index index.html
server_name subdomain.domain.com;
location / {
try_files $uri /index.html
}
location /api {
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_cache_bypass $http_upgrade;
}

Resources