Deploy Create-React-App on Nginx - nginx

I'm attempting to deploy my create-react-app SPA on a Digital Ocean droplet with Ubuntu 14.04 and Nginx. Per the static server deployment instructions, I can get it working when I run serve -s build -p 4000, but the app comes down as soon as I close the terminal. It is not clear to me from the create-react-app repo readme how to keep it running forever, similar to something like forever.
Without running serve, I get Nginx's 502 Bad Gateway error.
Nginx Conf
server {
listen 80;
server_name app.mydomain.com;
root /srv/app-name;
index index.html index.htm index.js;
access_log /var/log/nginx/node-app.access.log;
error_log /var/log/nginx/node-app.error.log;
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm|svg)$ {
root /srv/app-name/build;
}
location / {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Access-Control-Allow-Origin *;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

One of the major benefits of React (and Create React App) is that you don't need the overhead of running a Node server (or proxying to it with Nginx); you can serve the static files directly.
From the Deployment documentation you've linked to, Create React App describes what to do:
npm run build creates a build directory with a production build of your app. Set up your favorite HTTP server so that a visitor to your site is served index.html, and requests to static paths like /static/js/main.<hash>.js are served with the contents of the /static/js/main.<hash>.js file.
In your case, run npm run build to create the build/ directory and then make the files available in a location Nginx can access them. Your build is probably best done on your local machine and then you can securely copy the files across to your server (via SCP, SFTP etc). You could run npm run build on your server, but if you do, resist the temptation to directly serve the build/ directory as the next time you run a build, clients could receive an inconsistent set of resources whilst you're building.
Whichever build method you choose, once your build/ directory is on your server, then check its permissions to ensure Nginx can read the files and configure your nginx.conf like so:
server {
listen 80;
server_name app.mydomain.com;
root /srv/app-name;
index index.html;
# Other config you desire (TLS, logging, etc)...
location / {
try_files $uri /index.html;
}
}
This configuration is based upon your files being in /srv/app-name. In short, the try_files directive attempts to load CSS/JS/images etc first and for all other URIs, loads the index.html file in your build, displaying your app.
For note, you should be deploying using HTTPS/SSL to serve it rather than with insecure HTTP on port 80. Certbot provides automatic HTTPS for Nginx with free Let's Encrypt certificates, if the cost or process of obtaining a certificate would otherwise hold you back.

I was hosting NextJS as main app on / and wanted to host CRA on /admin route. Here is what I did:
serve CRA through custom express server
change hostname in package.json
add basename to /admin for react-router
define the following proxy pass:
location / {
proxy_pass http://localhost:3000;
}
location /admin {
proxy_pass http://localhost:3001;
}
location /admin/ {
proxy_pass http://localhost:3001/;
}
Related articles:
CRA Deployment
React-Router
Multiple SPAs
Another StackOverflow question

Related

Routing a Meteor/React app to give it a plain vanilla HTML home page

I want to use Meteor with React to serve a web site, but I want the default home page to be plain vanilla HTML. Like this:
https://meteor.example.com/ => HTML page with no JavaScript
https://meteor.example.com/meteor/ => fully reactive Meteor experience
I understand that I can put a plain index.html page in the /public folder, but then it needs to be addressed specifically as https://meteor.example.com/index.html.
I have not been able to find a tutorial that addresses this particular situation.
I found a simple solution which does not require any changes to the Meteor app. I'm using nginx to serve the Meteor site. I get the app to run on a local port, and set up a proxy rule in nginx:
server {
listen 80;
server_name example.com;
charset utf-8;
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;
}
}
As I understand it, this passes the url request to the Meteor app. If there is an index.html file in the public/ folder, this will be served instead of the Meteor app when a request is made for http://example.com/.
(If I use location / instead of location ~ /(.+), as I was doing originally, then a request for http://example.com/ is treated as a request for the default page from Meteor.)
DEPLOYING
For production, I create a package using...
meteor build --server-only ../example-package
... which I upload to the server and unzip. I then cd into bundle/programs/server and run npm install --production as the user which will be running the app. I then add folders asbundle/tmp/ and bundle/public/ and put my static index.html file and all the CSS and other files it requires in the public/ folder.
Currently I am using export ROOT_URL=http://localhost && export PORT=3000 && node main.js to start the app, but I plan to use a Phusion Passenger Standalone as soon as I have jumped through all the right hoops.
Here is an example of using WebApp with Assets
The following code would go into a server side only file for example /server/main.js in a meteor project
WebApp.connectHandlers.use('/home', (req, res, next) => {
res.writeHead(200, {'Content-Type': 'text/html'});
const html = Assets.getText('home.html');
res.end(html);
});

nginx responds with 404 Not Found (Single Page App)

I have a Single Page Application with regular Browser Router (without hash). Whenever someone navigates through page and hits refresh button nginx tries to find file on this path. So if someone is on mypage.com/about nginx looks for about file and responds with 404 Not Found. How to fix this issue?
I'm thinking about specifying a location with wildcard - mypage.com/* except /api tho, because every backend endpoint in this app starts with api. How to match all paths except one? This is how my config looks like:
upstream frontend {
server frontend:3000;
}
upstream backend {
server backend:8000;
}
server {
listen 80;
location /api {
proxy_pass http://backend;
proxy_set_header Host \$http_host;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
}
location / {
proxy_pass http://frontend;
proxy_redirect default;
}
}
Why do your proxy requests for frontend app ? I assume that you are using some kind of development server to serve your frontend application. It is better to build your frontend application to static files and serve them as regular static files, without any server except the nginx.
As for your question, if you will build your frontend application into static files you may configure location in nginx like this:
root /var/www/your_site;
location / {
try_files $uri /index.html;
}
where index.html is entrypoint into your application and the root path should be configured to place where it stored.
If you still want to serve frontend application from development server through nginx you may configure nginx to handle errors from upstream and point error page to root of dev server.
In this case following directives should help you:
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors
http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page

Set up a Ghost blog at /blog on Meteor

I am not sure if this is possible but there is a way to host a Ghost blog at a subfolder instead of a subdomain https://www.allaboutghost.com/how-to-install-ghost-in-a-subdirectory/
I have set up everything on that end the way it says and now the only thing that is needed is to exclude /blog from the FlowRouter.notFound function. is there a way to do that or set up the route to listen to nginx?
// EDIT
Here's the nginx config
server {
listen 80;
server_name localhost;
location ^~ /blog {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:2368;
proxy_redirect off;
}
}
here's ghost config
config = {
// ### Production
// When running Ghost in the wild, use the production environment.
// Configure your URL and mail settings here
production: {
url: 'https://www.raiseyourflag.com/blog',
//everything else
}
}
There are two separate issues here.
Setting up Ghost to be served a subdirectory through Nginx. The tutorial you linked to covers exactly that.
Integrating the blog with a second site based on Meteor.
It's not clear if you've completed the first step or not, so let's make sure that's taken care of first:
# Make sure your config.js for Ghost includes /blog in the url key in the development section: 'http://127.0.0.1:2368/blog'
# Start Ghost in in the development enviroment: NODE_ENV=development node index.js
# Check that something is actually running on port 2368: sudo netstat -plnt | grep ':2368'
# Go here in your browser, you should see your Ghost blog and be able to browse it: http://127.0.0.1:2368/blog
If you have gotten that far, your Ghost blog is working and you are ready to access it through Nginx on port 80. To simplify the problem for this step, move any Meteor code out of the way temporarily so this can be verified.
Your Nginx configuration looks good. Just reload Nginx once more for
good measure, and then check this URL in your browser now:
http://127.0.0.1/blog
Now you should see your Ghost blog again, but now accessed through Nginx and proxied to the other port.
Once you've confirmed that step is working, add put the Meteor frontend code back in place. From the perspective of any frontend code, /blog is just like any URL handled by the web server.
If you go to /blog and see a NotFound page served by Meteor, that means that the client-side Meteor framework must have loaded from /somewhere/, presumably /index.html. In this case, there's a problem with the Nginx configuration. Perhaps there is more to it whant you have posted?

NGINX not responding

I have three sites configured on my server using NGINX and the first two are working fine. One is a static site and one is running Rails (using Unicorn). I have attempted to mirror the NGINX/Unicorn configurations.
For the non-working site, I get "problem loading site" in my browser and absolutely nothing in my NGINX error logs (even at debug level) or my Unicorn log. I also get nothing when I attempt to cURL to the site.
I have double checked DNS by pinging domain name and am running out of ideas. I've also tried making this the default server and browsing by IP address.
Thoughts on how I should go about debugging? I would like to at least understand if NGINX is seeing these requests or not.
NGINX configuration:
upstream unicorn-signup {
server unix:/home/signup/app/tmp/sockets/unicorn.sock;
}
server {
listen 80;
listen [::]:80;
root /home/signup/app/current/public;
server_name signup.quote2bill.com;
# configure for Unicorn (NGINX acts as reverse proxy)
location / {
try_files $uri #unicorn;
}
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded_Proto $scheme;
proxy_redirect off;
proxy_pass http://unicorn-signup;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
Fixed! It was the dreaded force_ssl flag in my production configuration. For future travelers, here is how I went about troubleshooting:
Went on a Costco run to clear my mind and buy huge quantities of stuff.
To determine if it was a DNS, NGINX or Unicorn/Rails problem, I replaced my NGINX configuration with a very simple one and placed a simple index.html in my public root. This worked fine - which lets DNS off the hook (I could resolve the domain name at the web server).
I diff'd the working and non-working NGINX configuration files for the nth time and made them as close as possible but didn't find anything.
Then I noticed that when I was serving the simple index.html file in #2 above, the domain was not getting redirected to https:// but when switched to my "normal" Unicorn/Rails version, I was always getting redirected.
I searched for Rails redirecting to SSL and remembered the force_ssl flag.
I checked my two projects and noticed the flag was not set in the working project, but set in the non-working one (smoking gun).
I changed, committed, redeployed and reloaded the browser and it... didn't work (!) Fortunately, I had the good sense to clear browser cache and try again and it is all good now.
Hope this helps someone.

docker-registry nginx rest api

I am trying to build a docker-registry server from source (not as a container) on Ubuntu 14.04.1. I was able to get most of the way there using the instructions found on digitalocean.
I am able to curl http://localhost:5000 and https://user:password#localhost:8000 with no problems
When I try to open a web browser to see hopefully more than just that, that is when the issues seem to happen.
Here is my docker-registry file in /etc/nginx/sites-available/:
# For versions of Nginx > 1.3.9 that include chunked transfer encoding support
# Replace with appropriate values where necessary
upstream docker-registry {
server 192.168.x.x:5000;
}
server {
listen 8000;
server_name docker-registry;
ssl on;
ssl_certificate /etc/nginx/ssl/docker-registry.crt;
ssl_certificate_key /etc/nginx/ssl/docker-registry.key;
proxy_set_header Host $http_host; # required for Docker client sake
X-Real-IP $remote_addr; # pass on real client IP
client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads
# required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
chunked_transfer_encoding on;
location / {
# let Nginx know about our auth file
auth_basic "Restricted";
auth_basic_user_file docker-registry.htpasswd;
proxy_pass http://docker-registry;
}
location /_ping {
auth_basic off;
proxy_pass http://docker-registry;
}
location /v1/_ping {
auth_basic off;
proxy_pass http://docker-registry;
}
}
I have my docker registry stored locally in /var/docker-registry and ensured that it was readable by the www-data user. Why can I not see my images on the web browser?
If I tag an image and push it to my repository it works, I can see it in the web browser:
https://192.168.x.x:8000/v1/repositories/ubuntu-test/tags/latest
I see the following:
"5ba9dab47459d81c0037ca3836a368a4f8ce5050505ce89720e1fb8839ea048a"
When I try to get to:
https://192.168.x.x:8000/v1
Or:
https://192.168.x.x:8000/v1/repositories
Or:
https://192.168.x.x:8000/v1/images
I get a "not found" error
How would I be able to see everything in my /var/docker-registry folder (which is where these are stored....and yes, they are owned by the www-data user) through the web interface?
This is by design. Not only is there no reason one would implement the entire url path, but there are severe security implications with implementing it.
I'm assuming you don't have much experience with web programming. There is no directory '/v1/repositories'... etc. Instead, there is a program (in this case either Python or Ruby) that is listening for the url path and has logic built-in to determine what to do.
i.e. if url = /v1/_ping: return 'ok'

Resources