Angular 7、NGINX和Docker刷新页面时出现404错误

41

我有一个用Angular 7编写的应用程序,我正在使用NGINX将其部署到Docker容器中。当我运行容器时,一切都运行得很完美,但是如果我在浏览器中刷新页面(按F5),我会得到一个NGINX 404错误页面。

下面是我的nginx.conf文件,您可以看到我尝试使用"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;
        }
    }
}

我的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;"]

部署容器上的目录为:

/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

这里是控制台输出:

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"

这里发生了什么事情,有任何想法吗?

更新:实际答案在@Rajesh的回答评论中。问题在于我正在处理/etc/nginx/nginx.conf,而我需要处理/etc/nginx/conf.d/default.conf。


希望那个投反对票的人能解释一下问题出在哪里,这样就可以改进了。 - Andrew Harris
5个回答

92

在刷新Angular应用程序时,我们需要告诉nginx Web服务器在显示错误页面之前首先查看index.html文件是否存在所请求的路由。对我来说,这很有效:

nginx.conf

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

Dockerfile

FROM node:16-alpine as node
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build --prod

FROM nginx:alpine
COPY ./nginx.conf /etc/nginx/conf.d/default.conf # Not /etc/nginx/nginx.conf
COPY --from=node /app/dist/myapp /usr/share/nginx/html

1
请查看我的nginx.config文件。我已经在里面使用了try_files,但它不起作用。请注意,我没有“folder”检查,但根据文档,“使用try_files意味着您可以测试一个序列。如果$uri不存在,则尝试$uri/,如果不存在则尝试回退位置。”和“如果您不关心目录的存在性检查,可以通过删除$uri/来跳过它。”- https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/ - Andrew Harris
@AndrewHarris 或者我可以分享完整的 default.conf 文件,它是工作正常的...让我知道。 - rajesh-nitc
2
我刚刚得出了完全相同的结论。问题在于我正在处理 /etc/nginx/nginx.conf 文件,而我需要处理的是 /etc/nginx/conf.d/default.conf 文件。 - Andrew Harris
3
try_files $uri $uri/ =404; 更改为 try_files $uri $uri/ /index.html; 对我起了作用。 - benjamin mwalimu
点赞: 这个顺序是关键的.. 1) 根目录 /usr/share/nginx/html; 2) 尝试文件 $uri $uri/ /index.html; 3) 默认文件 index.html index.htm; - Yensee
显示剩余4条评论

12

通过简单地使用 useHash: true 标记,很可能可以快速解决这个问题。由于某种未知原因,Angular没有将此设置默认为true。

请确保你的 app-routing-module.ts 文件包含如下所示的 useHash:

@NgModule({
  imports: [RouterModule.forRoot(routes, { useHash: true })],
  exports: [RouterModule]
})

3

我有一些经验可以补充,最近我的一个应用程序遇到了这个问题,但是似乎没有任何方法能够解决它。我发现每个版本的Nginx都略有不同。

在我的情况下,解决此问题的方法是在 /etc/nginx/conf.d/default.conf 中添加以下形式的404回退,因为我的 nginx.conf 写法本身就有问题。

下面是 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;
        }

    }
}

通常情况下,您可以删除该行以不考虑default.conf或任何其他文件,这对于大多数docker化的容器来说是可以的(但不适用于我的情况!),并且似乎适用于许多其他场景。


3

在我的情况下,我有一个NGINX作为代理前端,它面向一组NGINX服务器,并且我必须添加=404来避免重定向循环:

location / {
    root        /usr/share/nginx/html;
    index       index.html;
    try_files   $uri $uri/ /index.html =404;
}

2

Rajesh的答案很好,但我认为应该加上一些代码来扩展。

根据当前的答案,例如,如果找不到一个javascript文件,将返回index.html,这可能导致浏览器将index.html文件缓存为您请求的javascript文件的名称。如果发生这种情况,您的应用程序很可能会崩溃,您需要清除缓存才能使其再次正常工作。

为了防止这种情况,您可以添加一个位置块来处理javascript文件,如果找不到该文件,则返回404错误。请参考下面的代码。

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

注意:此示例是使用javascript文件,但您可以通过在位置块中扩展正则表达式来添加任何您希望在找不到文件时具有404行为的文件扩展名。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接