I'm setting up my MERN project on my production server and whilst
making sure you can manually type in URL (such as myproject.com/dashboard) I added the following line to the server section of my Nginx configuration file try_files $uri /index.html; to allow this(as stated by the react-router training page). This has now caused the following response when trying to login Cannot POST /index.html.
If I remove the line all calls to the api work(i can login again) but I cannot enter url manually.
I've tried moving the try_files line to the top of the server section incase the server section is sensitive to this.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name myproject.com;
root /home/forge/myproject.com/client/build;
...
location / {
try_files $uri /index.html;
proxy_pass http://127.0.0.1:8080;
}
...
}
const express = require('express');
const app = express();
app.use( express.static( `${__dirname}/client/build` ) );
app.use('/api/users', usersRouter);
app.use('/api/playlists', playlistRouter);
app.get('*', (req, res) => {
res.sendFile(`${__dirname}/client/build/index.html`);
});
I expect to be able to login(make calls to my api) and enter URLs manually to my project.
I think your configuration is not valid. In your config if requested file does not exists you are sending the file index.html no matter what. Will never call proxy.
Since your server has /api prefix configure that on your nginx server like this. So request starts with /api will be proxy to your backend server.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name myproject.com;
root /home/forge/myproject.com/client/build;
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
location / {
try_files $uri /index.html;
}
}
Related
I have a React app with an nginx conf as below. This app communicates with a backend (represented here with the alias app-backend in the conf file).
upstream app-backend {
server app-backend.domain.com;
}
server {
listen 3000;
root /usr/share/nginx/html;
location /api/ {
proxy_pass https://app-backend/;
proxy_redirect default;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
In the React app, when a user clicks the login button, this code is executed:
window.location.replace('/api/auth/login');
return false;
Redirection to the backend does not work though; in the frontend, I get a 404 Not Found nginx/1.17.8 and the frontend UTL changes to app-frontend.domain.com/api/auth/login
By the way, when I test this locally, redirection to my backend is successful, and the authorization server is called as well.
I have a Django backend and Vue.js frontend served by the same machine. I want a specific path to be directed to the django API and admin panel, while the rest should load Vue index.html and be passed to Vue router. My problem is, that while the base path is passed on to Vue.js, and my selected path does go through to Django, any other path results in 404, which means that if I want to revisit a path generated though Vue router, it's impossible to do so.
I figured the problem must be somewhere in the NginX config file:
# the upstream component nginx needs to connect to
upstream myproject {
server unix:///tmp/myproject .sock;
}
server {
listen 80;
listen [::]:80;
server_name serverurl.com;
return 301 https://serverurl.com$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /home/projects/myproject/ssl/ANY.serverurl.com.crt;
ssl_certificate_key /home/projects/myproject/ssl/ANY.serverurl.com.key;
server_name serverurl.com www.serverurl.com;
access_log /home/projects/myproject/logs/nginx_access.log;
error_log /home/projects/myproject/logs/nginx_error.log;
location / {
root /home/projects/insights/vue/dist;
try_files $uri $uri/ /home/projects/myproject/vue/dist/index.html =404;
}
location /backend/ {
include /etc/nginx/uwsgi_params;
uwsgi_pass myproject;
}
location /static/ {
alias /home/projects/myproject/static/;
}
location /media/ {
alias /home/projects/myproject/media/;
}
}
So the "/" path results in correctly rendering Vue index.html
And "/backend/" correctly loads django urls
But, for example, if the user tries to access "/" and is not logged in, he will be redirected to "/login" by Vue router, which works fine and the login page works correctly, and the user is redirected back to "/". But, if the user tries to access "/login" directly, or any other route such as "/options", "/profile", etc..
I have tried to use some kind of regex, but could not figure it out unfortunately, and resulted in an infinite redirect to "/index.html/index.html..." until the browser blocked the redirect.
Here is my attempt: I added this location to the end of config:
location ~* ^/(.*) {
index /home/projects/myproject/vue/dist/index.html;
}
Lets look at your try_files directive:
try_files $uri $uri/ /home/projects/myproject/vue/dist/index.html =404;
So what happened here when your user tries to access the /login URI directly?
nginx processed first try_files parameter $uri (which is equals to /login), tries to check the /home/projects/insights/vue/dist/login file existence and fails;
nginx processed second try_files parameter $uri/, tries to check the /home/projects/insights/vue/dist/login/ directory existence and fails;
nginx processed third try_files parameter /home/projects/myproject/vue/dist/index.html, tries to check the /home/projects/insights/vue/dist/home/projects/myproject/vue/dist/index.html file existence and fails;
nginx returns HTTP 404 Not Found code.
As it stated by the documentation, last parameter of the try_files directive can be
a new URI;
HTTP error code: =code;
named location ID: #location_name.
You need to process all requests for the non-existed files with your Vue app index.html file, so change your try_files directive to use /index.html as new URI if all other checks fails:
try_files $uri $uri/ /index.html;
(and I'm not sure you need that $uri/ part at all, maybe just try_files $uri /index.html; would be enough).
Additionally, I recommend you to use root /home/projects/myproject instead of alias /home/projects/myproject/static/ and alias /home/projects/myproject/media/ directives. As it said by the alias directive documentation:
When location matches the last part of the directive’s value:
location /images/ {
alias /data/w3/images/;
}
it is better to use the root directive instead:
location /images/ {
root /data/w3;
}
That way you can simplify your configuration to
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /home/projects/myproject/ssl/ANY.serverurl.com.crt;
ssl_certificate_key /home/projects/myproject/ssl/ANY.serverurl.com.key;
server_name serverurl.com www.serverurl.com;
access_log /home/projects/myproject/logs/nginx_access.log;
error_log /home/projects/myproject/logs/nginx_error.log;
root /home/projects/myproject;
location / {
root /home/projects/insights/vue/dist;
try_files $uri /index.html;
}
location /backend/ {
include /etc/nginx/uwsgi_params;
uwsgi_pass myproject;
}
location /static/ {}
location /media/ {}
}
I have made a full-stack app that has a frontend made from React and a server.js written in nodejs(Express) and I am using Postgres as my database. I have done this before once and host my site on Heroku.
The difference is that on Heroku I had my frontend and backend on separate Urls so that in my react app I could do
fetch('http://localhost:3005/url',{
method: 'get',
headers: {'Content-Type': 'application/json'},
})
.then(response => response.json())
The above code is while I am running my react and node on my laptop through npm start.
My server.js would be
app.get('/url', (req, res) => {
db.insert({
page_url: req.body.urlInfo,
user_visit_id: req.body.visitID,
})
.into('pageurl')
.then((id) => {
res.json(id);
}).catch(err => res.status(400).json('unable to write'))
})
In Heroku, I would just change the frontend React line to :
fetch('http://**Ther URL where my server.js is**/url',{
method: 'get',
headers: {'Content-Type': 'application/json'},
})
.then(response => response.json())
And this would run absolutely fine.
The issue comes now that I have to deploy the app to the school server. The server has nginx running and I have been given a domain for this project.
The front end is hosted on the nginx and whenever I vist my domain the frontend showsup but how do I connect it to my server.js endpoints given that I have to have the server.js in the same server as the react app which is hosted by the nginx. I am completely new to nginx and don't have a concrete idea about solving the problem. I have my node server.js running on the server using pm2.
But this is right now of little use, as my frontend fetch does the localhost:3005/url which looks for the url point in port 3005 of the device that visits the domain rather than the actual server that is hosting the frontend and the node server.js.
I also tried replacing the localhost:3005/url from the server.js to using the server's ip but that address only seems to be visitable if I am on the school's wifi connection.
If I change enter that ip address from my phone with a data connection, the address can't be reached. So, I assumed that "ipaddress:3005/url" is also not the correct way to do it. I am lost at this point and any sugesstions would be a great help!!! Ideally I would like to run host my frontend on nginx on the server and have pm2 running the server.js.
I just need a way to connect the endpoints of the frontend and the backend. I need the
URL where my server.js is part in
fetch('http://Ther URL where my server.js is/url',{
method: 'get',
headers: {'Content-Type': 'application/json'},
})
.then(response => response.json())
My nginx config file right now looks like this:
server {
listen 80;
listen [::]:80;
root /var/www/example.com/html/build;
index index.html index.php index.htm index.nginx-debian.html;
server_name example.com www.example.com;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}
location / {
try_files $uri /index.html;
#try_files $uri $uri/ =404;
}
}
This nginx configuration only serves the frontend at the moment. Also, the real domain.com is replaced by example.com in the above code.
Thanks!
What i would do is setup a virtual host in nginx on a different port, which proxies all requests to your nodejs app, like:
server {
listen 8080;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3005;
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;
}
}
In this example, the following assumptions have been made:
example.com is the domain that the school assigned to your project
port 8080 is the port that you will open and set in your front-pages: fetch('http://example.com:8080', {})
your nodejs app runs on port 3005
Edit 1: Here is a mini code I made that reproduces the error. Please follow README.md to install.
Edit 2: Finally, I found one solution. Besides $locationProvider.html5Mode(true); ($locationProvider.hashPrefix('') is NOT necessary for me) and <base href="/" />, I need to add the follows in routes/index.js, rather than app.js. Then, we do NOT need to add anything more to app.js or nginx or apache like this thread mentions.
var express = require('express');
var router = express.Router();
var path = require('path');
... ...
router.get('*', function(req, res) {
res.sendfile('./views/index.html'); // load our public/index.html sendFile
// res.sendFile('index.html', { root: path.join(__dirname, 'views') }); // does not work
});
One problem is, in the server console, it gives express deprecated res.sendfile: Use res.sendFile instead routes/index.js:461:9. But res.sendFile('index.html', { root: path.join(__dirname, 'views') }); can not help, it returns 404 error.
My express version is ~4.14.0... does anyone know how to fix that?
OP:
I develop in Mac with Apache a MEAN-stack application that can be requested by
https://localhost:3000/#/home. In production with an NGINX server, the application can be requested by
https://www.myapp.io/#/home. The fragment-identifier # is needed in all cases because of angular ui-router.
So I wanted to make pretty url without # (eg, https://www.myapp.io/home, https://localhost:3000/home) work. I have done the following:
added $locationProvider.html5Mode(true); $locationProvider.hashPrefix('') in app.config(['$stateProvider'....
added <base href="/" /> in index.html
As a result, https://localhost:3000/#/home changes automatically to https://localhost:3000/home in the browser bar, similarly for https://www.myapp.io/#/home.
However, directly entering https://localhost:3000/home or https://www.myapp.io/home in the browser will raise an error (I don't know how to turn previous <h1><%= message %></h1><h2><%= error.status %></h2><pre><%= error.stack %></pre> in error.ejs to error.html, so I don't have more details).
So now, the goal is to make https://localhost:3000/home and https://www.myapp.io/home work.
By following this thread, I added the follows to app.js:
app.use('/js', express.static(__dirname + '/js'));
app.use('/dist', express.static(__dirname + '/../dist'));
app.use('/css', express.static(__dirname + '/css'));
app.use('/partials', express.static(__dirname + '/partials'));
app.all('/*', function(req, res, next) {
res.sendFile('index.html', { root: __dirname });
});
And in Apache of Mac, here is my httpd-vhosts.conf, after restarting apache,
https://localhost:3000/home still returns an error.
<VirtualHost *:443>
ServerName localhost
DocumentRoot "/Users/SoftTimur"
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile /etc/apache2/ssl/localhost.crt
SSLCertificateKeyFile /etc/apache2/ssl/localhost.key
<Directory "/Users/SoftTimur">
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.html [L]
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>
</VirtualHost>
In production, here is the NGINX server block. After restarting NGINX, https://www.myapp.io/home still returns an error.
server {
listen 443 ssl;
server_name myapp.io www.myapp.io;
ssl_certificate /etc/letsencrypt/live/myapp.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.io/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:EC$
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;
index index.html;
root /opt/myapp;
location / {
try_files $uri $uri/ /index.html;
}
location ~ /.well-known {
allow all;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Accept-Encoding "";
proxy_set_header Proxy "";
proxy_pass https://127.0.0.1:3000;
# These three lines added as per https://github.com/socketio/socket.io/issues/1942 to remove sock$
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Could anyone help?
This might useful stuff,
AngularJS routing without the hash '#'
Also, use this line in your express server file.
app.use(express.static(path.join(__dirname, 'client folder')));
this will directly finds your index.html file in that views folder and loads it
try this in your express server
var express = require('express');
var app = express();
app.get('*', function (req, res) { res.sendFile(__dirname + '/views/index.html'); });
and in your angular app:
$locationProvider.hashPrefix('!').html5Mode({
enabled: true
});
$urlRouterProvider.otherwise('/');
and you still need the <base href="/"> in your index.html
let me know if this works for you
EDIT:
I just found your app in https://github.com/chengtie/mini-mean,
looks like your app.use order is wrong. Please copy paste this in your express server and check if it's okay now. pastebin
You don't need Apache or Nginx to run NodeJs in development, just node server.js is enough
Express gave you that error because you are using a deprecated API res.sendfile please use res.sendFile (capital F)
Some info for doing SPA:
When you have the '#' in your URL, the browser interpret it as a local reference and thus, won't send new request to the server
By enabling $locationProvider.html5Mode(true) you are now using html5 push state to navigate around your application history and (if I'm not mistaken you are happening to be using) angular effectively remove the '#' in url
Without the '#' (hash-bang) browser will interpret it as a new request and send it to server, so you have to map all requests from server to your SPA entry file
For exact steps of replicating this behavior, consult this article: https://scotch.io/tutorials/pretty-urls-in-angularjs-removing-the-hashtag (the base href in your entry file is important)
If it's just about the #. You can remove it in Angular itself.
Just inject locationProvider at your app entry and set htmlMode to true. In your index.html set the baseUrl.
$locationProvider.html5Mode(true)
And in you index.html add:
<base href="/" />
That will generate your urls without the #. Does that help?
Good day, i want handle php file and html on same server, i want that html files handle node on port 8080, and php files handle php5 service. I wrote nginx config like this:
server {
listen 80 default_server;
root /home/examle/public_html;
index index.php index.html index.htm;
server_name www.examle.com examle.com;
location ~ \.html$ {
proxy_pass http://localhost:8080;
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_buffering off;
}
location / {
try_files $uri $uri/ =404;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
but when html pages opened nodejs didn't handle node javascript which contains in html page. How can i handle both type of files php and html on same server?
It won't work this way.
The thing is: when you use nginx+php, nginx doesn't really run php filename.php when you hit localhost/filename.php. There is php-fpm and nginx sends request to it. Then, php-fpm runs actual script, creating proper $_SERVER, $_GET, etc.
In node case, you don't have any node-fpm service. Nginx won't run node filename.html. You need to set up the real node process at 8080 port that serves http connections, because all nginx does is it "passes" an actual http connection to the http://localhost:8080, as you specified.
Learn how to get node http server working.
P.S. Clue: look at your nginx logs more often (/var/log/nginx/* at Ubuntu/Debian). :)