NginX location management with Django&Vue.js setup - javascript

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/ {}
}

Related

Return 404 for invalid routes via nginx

I am using nginx with react.
My nginx.conf file
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
}
As per this config nginx is serving 200 for all routes..
Suppose my routes are
example.com/login
example.com/landing
Now suppose someone enter wrong url
example.com/test
In that case i want to throw 404 without landing to application at nginx level itself. Is this possible to handle routing at nginx level and send 404 despite of 200 and than handling at react level.
I found that to restrict any particular route at nginx level we can do it using below code
location ^~ /test/ {
return 404
}
But i want to restrict all routes which are invalid for that i used
# return 404 for all routes
location / {
return 404;
}
# define each possible route like this
location /login {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
location /landing {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
But after this my application is not loading getting 503 Service Temporarily Unavailable
First question is it really possible to achieve routing at nginx level if yes what approach i need to take any clue or reference will be of great help.
Thanks in advance
EDIT
Please find docker file
# Base Image
FROM node:10-alpine AS base
MAINTAINER test#test.com
RUN mkdir -p /usr/src/app
COPY . /usr/src/app
# Dependencies
FROM base as build
WORKDIR "/usr/src/app"
RUN npm install
RUN npm run test
RUN npm run build
# Web Server
FROM nginx:alpine
EXPOSE 80
COPY --from=build /usr/src/app/build /usr/share/nginx/html
COPY --from=build /usr/src/app/nginx.conf /etc/nginx/conf.d/default.conf
Yes, nginx can be used for this.
COPY --from=build /usr/src/app/nginx.conf /etc/nginx/nginx.conf
Just use this one.

Deploying React js build directory in nginx using kubernetes

Deploy React js(build directory) static files in Nginx server.
Using kubernetes, i am running the nginx server to serve the React js "build" directory for static files. I could access the simple index.html in the browser. But not the index.html with react js code. The root directory has the index.html file, static directory and other files in it. Directory structure is copied to this path.
nginx.conf is like below
events {
worker_connections 1024;
}
http {
server {
listen 80;
root /usr/share/nginx/html;
#index index.html;
location / {
index index.html;
try_files $uri $uri/index.html /index.html;
}
location /static/ {
autoindex on;
root /usr/share/nginx/html/static/;
}
}
}
In the browser, index.html is not loaded, but in source (right-click in the browser > select source), the index.html code is available. formatted index.html file copied from source in the browser is at this path. I am predicting that javascript(css,js) code is not executed or having issues loading in the browser. Otherwise, there is a problem with the nginx configuration to load the javascript files.
how to properly configure nginx to deploy Reactjs build directory files?
try_files does not tell nginx to serve the static file. Reaching the closing brace in the absence of any other operation causes it to serve the static file. try_files tests for the existence of the file in the local file system and may rewrite the URL.
You have syntax errors in your file i.a white spaces.
Remember to create service for nginx - type LoadBalancer and refer to it in deployment configuration file. You have to add your server if there are several servers that match the IP address and port of the request.
Here is example how you should execute this process. Make sure you followed every step from tutorial: nginx-configuration - there is example of exercise on PHP application but there are the same steps to reproduce with ReactJS app.
Your conf file should looks like this:
events {
worker_connections 1024;
}
http {
server {
listen 80;
root /usr/share/nginx/html;
index index.html index.htm;
server_name your_server_name_or_IP
location / {
try_files $uri $uri/index.html /index.html;
}
location /static/ {
autoindex on;
root /usr/share/nginx/html/static/;
}
}
}
The below code that works for me.
server {
listen 80;
root /usr/share/nginx/html;
index index.html index.htm;
server_name xyz.application.com;
server_name localhost;
location / {
ssi on;
set $inc $request_uri;
if (!-f $request_filename) {
rewrite ^ /index.html last;
}
index index.html;
try_files $uri $uri/index.html /index.html =404;
}
Setting the rewrite redirects to index.html page. I am not sure about the impact of ssi, set $inc $request_uri in the code.

How to fix 'Cannot POST /index.html' in Nginx, Express and NodeJS

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

Laravel 5.4 Served in subdirectory Nginx. 404 when including js files

My problem is serving CSS and JS files using Laravel 5.4 on an Nginx server (Ubuntu). I have configured the server to serve the application from a nested sub-directory (not in the root of the web server). Routing is working. See:
https://zwestwind.com/sandboxes/zypth/blog/tasks
https://zwestwind.com/sandboxes/zypth/blog/posts
and just ..blog/ as well.
The problem is that I cannot include JS files (and likely css). The JS file is in "blog/public/js/posts.js".
Attached below is my site configuration, and the code to include a JS file in a blade template. You can view the error in the console when you hit the site.
Server Configuration:
server {
return 404;
}
server {
listen 80;
listen [::]:80 ipv6only=on;
listen 443 ssl;
root /var/www/zwestwind.com/html;
index index.php index.html index.htm;
# Make site accessible from http://localhost/
#server_name example.com www.example.com;
server_name zwestwind.com www.zwestwind.com;
ssl on;
ssl_certificate /etc/nginx/ssl/pub_clf_origin.pem;
ssl_certificate_key /etc/nginx/ssl/priv_clf_origin.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
# Uncomment to enable naxsi on this location
# include /etc/nginx/naxsi.rules
}
# route to laravel project: $uri = /sandboxes/zypth/blog/public/index.php
location ~ ^/sandboxes/zypth/blog {
alias /var/www/zwestwind.com/html/sandboxes/zypth/blog/public;
try_files $uri $uri/ #laravel;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
#fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/zwestwind.com/html/sandboxes/zypth/blog/public/index.php;
}
}
location #laravel{
rewrite /sandboxes/zypth/blog/(.*)$ /sandboxes/zypth/blog/index.php?/$1 last;
}
# Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests
#location /RequestDenied {
# proxy_pass http://127.0.0.1:8080;
#}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
# With php5-cgi alone:
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
Blade code to include the JS file:
<script src="{{ URL::asset('posts.js', true) }}"></script>
Things that I have tried:
I have tried using 'js/posts.js' and many other variations, including hardcoded paths such as 'zwestwind.com/sandboxes/zypth/blog/public/posts.js' to no avail (with the protocol, I can only post 2 links due to reputation).
Adding a location block: location ~ .js$ {...} to add a content-header of application/x-javascript
I have configured a vhost server to serve the same application at (http)blog.zwestind.com/posts. All routing and inclusion of CSS/JS files works there, check it out. The server config for that sub domain is exactly as specified in the Laravel installation guide.
I am guessing this issue stems from a server config problem with the primary domain... ie) the location block listening for "^/sandboxes/zypth/blog" is catching the request for the JS file and modifying the URI, thus resulting in a 404. How would I fix this?
When the script works, it will say "posts.js is ready!" in the console (see the sub domain on http for an example).
Thanks.
P.S. If anyone is curious as to WHY I want to serve this app (and more) in a nested directory, it is because I want to host live demo apps using ONE SSL cert via ONE domain while also learning how to configure web servers. This server is not intended for heavy traffic.
Try including the file like this:
<script src="/js/posts.js"></script>
The "/" before the directory sends the request to the root (public folder), where the js and css directories are.
I found this better then adding new location root or aliases in the nginx config.
I do the same with images and it works fine.

How to redirect all Angular request to index.html in Nginx

I have create a simple Nginx config file to server an Angular like so:
server {
listen 80;
listen [::]:80;
root /path/to/apps/myapp/current/dist;
access_log /path/to/apps/myapp/current/log/nginx.access.log;
error_log /path/to/apps/myapp/current/log/nginx.error.log info;
index index.html;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location / {
try_files $uri $uri/ =404;
}
}
And all works fine as expected. because I'm using Angular UI Router, I'd like to forward all pages to index.html so Angular will take over (so a request to example.com/users/new will be redirect by Nginx to index.html, for Angular UI to handle it) for pages reloads, links, etc.
How can I achieve this?
I've playing around with options like:
server {
#implemented by default, change if you need different ip or port
#listen *:80 | *:8000;
server_name test.com;
return 301 $scheme://www.test.com$request_uri;
}
As specified in this answer. But I couldn't anything similar to work based on all requests.
Suggestions will be much appreciated.
You've nearly got it. try_files is the correct one to use, as Richard shows, you just needed to add your index.html in the list. However, there is no need to remove the =404 from the end, in fact, its better not to.
location / {
try_files $uri $uri/ /index.html =404;
}
Once the above change is implemented, reload the config with sudo nginx -s reload
$uri will try the request as a file, if this fails, it moves to the
next one.
$uri/ will try the request as a folder /index.html will
then be tried, which sends all requests to your index.html where your
angular app will be able to route things (also make sure you use
html5mode to avoid the use of # in the url.
=404 will be your final fallback, basically saying: I've tried this as a file, folder, and via index.html. If index.html fails/does not
exist for whatever reason, we will serve a 404 error.
Why =404?
If all goes well, the last one is never used, and routing will just try file, folder, angular app. And that would be it. But I see it as good practice to have the =404 on the end to cover all bases.
Important note about index.html catchall
Regardless of whether or not you use the =404 in try_files, by directing everything to index.html, you basically lose the ability to serve 404 errors from your webserver, unless index.html does not exist (which is where =404 comes in). This means that you will need to handle 'Not Found' urls and missing pages within Angular JS, and you need to be ok with the fact that the error page you use will return a HTTP 200 response, rather than 404. (This is because the moment you direct to the index.html, you've served a page to the user, thus 200).
For added reassurance on the above, google try_files angular js and you will find most examples incorporate =404.
References:
http://nginx.org/en/docs/beginners_guide.html#control
http://ajbogh.blogspot.ca/2015/05/setting-up-angularjs-html5-mode-in.html (just one example)
You need to add the router to the end of your try_files directive, like this:
location / {
try_files $uri $uri/ /index.html;
}
See this document for more.
location / {
try_files $uri$args $uri$args/ index.html;
}
This worked for me

Categories