'404 Error on page refresh with Angular 7, NGINX and Docker
I have an application written in Angular 7 that I am deploying to a Docker container with NGINX. When I run the container, everything works perfectly except that if i Refresh the page in the browser (F5) I get an NGINX 404 error page.
Here is my nginx.conf file from which you can see ive tried "try_files"
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri /index.html;
}
}
}
My Dockerfile:
FROM node:alpine as builder
RUN apk update && apk add --no-cache make git
WORKDIR /app
COPY package.json package-lock.json /app/
RUN cd /app && npm install
COPY . /app
RUN cd /app && npm run build
FROM nginx:alpine
RUN rm -rf /usr/share/nginx/html/* && rm -rf /etc/nginx/nginx.conf
COPY ./nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/dist/hyper-client-admin /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Directory on the deployed container is:
/usr/share/nginx/html # ls -la
total 23564
drwxr-xr-x 1 root root 4096 May 20 00:18 .
drwxr-xr-x 1 root root 4096 Mar 8 03:05 ..
drwxr-xr-x 2 root root 4096 May 20 00:18 assets
-rw-r--r-- 1 root root 290728 May 20 00:18 es2015-polyfills.js
-rw-r--r-- 1 root root 211178 May 20 00:18 es2015-polyfills.js.map
-rw-r--r-- 1 root root 997 May 20 00:18 favicon.png
-rw-r--r-- 1 root root 770 May 20 00:18 index.html
-rw-r--r-- 1 root root 114749 May 20 00:18 main.js
-rw-r--r-- 1 root root 115163 May 20 00:18 main.js.map
-rw-r--r-- 1 root root 241546 May 20 00:18 polyfills.js
-rw-r--r-- 1 root root 240220 May 20 00:18 polyfills.js.map
-rw-r--r-- 1 root root 6224 May 20 00:18 runtime.js
-rw-r--r-- 1 root root 6214 May 20 00:18 runtime.js.map
-rw-r--r-- 1 root root 1117457 May 20 00:18 styles.js
-rw-r--r-- 1 root root 1191427 May 20 00:18 styles.js.map
-rw-r--r-- 1 root root 10048515 May 20 00:18 vendor.js
-rw-r--r-- 1 root root 10505601 May 20 00:18 vendor.js.map
And here is the console output:
172.17.0.1 - - [20/May/2019:00:18:30 +0000] "GET / HTTP/1.1" 200 371 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"lopment\hyper-client-admin>
172.17.0.1 - - [20/May/2019:00:18:30 +0000] "GET /runtime.js HTTP/1.1" 200 6224 "http://localhost:81/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"
172.17.0.1 - - [20/May/2019:00:18:30 +0000] "GET /polyfills.js HTTP/1.1" 200 241546 "http://localhost:81/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"
172.17.0.1 - - [20/May/2019:00:18:30 +0000] "GET /main.js HTTP/1.1" 200 114749 "http://localhost:81/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/74.0.3729.157 Safari/537.36" "-"
172.17.0.1 - - [20/May/2019:00:18:30 +0000] "GET /styles.js HTTP/1.1" 200 1117457 "http://localhost:81/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"
172.17.0.1 - - [20/May/2019:00:18:30 +0000] "GET /vendor.js HTTP/1.1" 200 10048515 "http://localhost:81/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"
172.17.0.1 - - [20/May/2019:00:18:31 +0000] "GET /assets/logo-white.svg HTTP/1.1" 200 4519 "http://localhost:81/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"
172.17.0.1 - - [20/May/2019:00:18:31 +0000] "GET /favicon.png HTTP/1.1" 200 997 "http://localhost:81/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"
172.17.0.1 - - [20/May/2019:00:18:35 +0000] "GET /login HTTP/1.1" 404 188 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" "-"
2019/05/20 00:18:35 [error] 6#6: *4 open() "/usr/share/nginx/html/login" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /login HTTP/1.1", host: "localhost:81"
Any ideas whats going on here?
UPDATE: The actual answer to this lies in the comments of @Rajesh's Answer. The issue is that I was working on /etc/nginx/nginx.conf and I needed to be working on /etc/nginx/conf.d/default.conf
Solution 1:[1]
this likely can be fixed quickly by simply using the useHash: true flag. For some unknown reason angular does not default this setting to true.
Make sure your app-routing-module.ts file contains useHash like this:
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule]
})
Solution 2:[2]
I have an experience to add to this, I faced this recently with one of my applications where none of this seems to work, and it make me stumble upon records that the Nginx is slightly different with each release.
In my case, what solved the issue was a 404 fallback of the form below in the /etc/nginx/conf.d/default.conf due to inherent war my nginx.conf was written.
Below is the default.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html =404;
index index.html index.htm;
}
}
nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
//This here takes the deafaut.conf alongside
//possibly overwriting the server markings in the nginx.conf
include /etc/nginx/conf.d/*.conf;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html =404;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
You can typically remove the line to not take into consideration the default.conf or any other files and that would be fine for most dockerized containers(not in my case though!) and seems to work for many other scenarios.
Solution 3:[3]
In my case, I have one NGINX fronting a group of NGINX' as a proxy, and I had to add =404 to avoid a redirect loop:
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html =404;
}
Solution 4:[4]
The answer of Rajesh is great, but I think it should be extended with some code.
With the current answer if, for example, a javascript file can't be found, the index.html is returned instead, which can lead the browser to cache the index.html file under the name of the javascript file you requested. If this happens your app will most likely crash and you need to clear the cache to get it working again.
To prevent this you can add a location block for the javascript files that will return a 404 if this file isn't found. See code below.
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
# Add additional extension if you need it
location ~ \.(js)$ {
try_files $uri $uri/ =404;
}
location / {
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
}
Note: This example is with javascript files but you can extend the regex in the location block with any file extensions you want to have the 404 behaviour for when the file isn't found.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Rick |
| Solution 2 | Abhimanyu Singh |
| Solution 3 | yglodt |
| Solution 4 | R. de Ruijter |
