Error when refreshing the same page twice

Viewed 326

I'm trying to deploy an angular app on a remote server using nodejs and nginx. I built the application and I serve it with a node app. Nginx acts here as a reversed proxy. I can acess the website and navigate without an issue. However, when I try to refresh the current page, my browser canno't find it anymore : response get a 404.

But if I enter the domain in the url bar, I can access again to the website.

Does anyone know where I made a mistake ?

Here's the code and the config for the node app and for nginx :

var express  = require('express');
var app      = express();                               // create our app w/ express
var morgan = require('morgan');             // log requests to the console (express4)
var bodyParser = require('body-parser');    // pull information from HTML POST (express4)
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const mustacheExpress = require("mustache-express");

dotenv.config();

// configuration =================

mongoose.connect(process.env.DB_CONNECT, { useNewUrlParser: true }, () => {
    console.log("connected to DB !");
  });

app.use(express.static("../front-end/dist/website"));                 
app.use(morgan('dev'));                                         // log every request to the console
app.use(bodyParser.urlencoded({'extended':'true'}));            // parse application/x-www-form-urlencoded
app.use(bodyParser.json());                                     // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());

// Middleware
app.use(express.json());

// Templating
app.engine("html", mustacheExpress());

//import routes
const authRoute = require("./routes/auth");
const devisRoute = require("./routes/devis");
const messageRoute = require("./routes/message");
const actuRoute = require("./routes/actu");

//Routes middlewares
app.use("/api/user", authRoute);

app.use("/api/sendDevis", devisRoute);

app.use("/api/message", messageRoute);

app.use("/api/actu", actuRoute);


// listen (start app with node server.js) ======================================
app.listen(3000);


server {

        root /var/www/domain.com/html;
        index index.html index.htm index.nginx-debian.html;

        server_name domain.com www.domain.com;

        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;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; # m                                                         anaged by Certbot
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem; #                                                          managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot


}
server {
    if ($host = www.domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;

        server_name domain.com www.domain.com;
    return 404; # managed by Certbot

}

1 Answers

This is an easy trap to fall into when doing client-side routing with a Single Page App.

The trick is this: when you do you navigating in the browser, the browser is not making requests to your server. When you refresh the page, the browser does send the request to the server. Since you're making an SPA, all of the information about what's on the page is in your Javascript, and that Javascript is loaded in your index.html file. The only file that exists on your server is your root file, and the express.static middleware maps URLs onto your files, so there is no file for it to find (hence the 404).

What you need server-side is to always serve up your index.html file no matter what html file is requested.

The easiest solution is add a fallback handler last in your routing using Express's build-in .sendFile() function. The simple, nuance-free way would be something like this:

// ... ^^ all your other routes
router.get("*",
  // The '*' means match *everything*
  (req,res)=>{
    res.sendFile('index.html'); // Probaby need to set some options here to start in the right directory...
  }
);

You may want to use different middleware or add your own bells and whistles for including compression, etc, but the idea would be the same.

Related