Express + Nginx. 无法提供静态文件服务

16

这是我的项目文件夹

/
  public/
    index.html
    main.js
    adaptor.js
    main.css
  node_modules/
    socket.io/
  index.js

这是我的index.js中的静态文件配置

app.use(express.static(path.join(__dirname, '/public')));
app.use(express.static(path.join(__dirname, '/node_modules')));
app.get('/', (req, res)=>{
    res.sendFile(path.join(__dirname, 'public', '/index.html'));
})

这是我的index.html文件。

  <script src="/socket.io/socket.io.js" charset="utf-8"></script>
  <script src="/adapter.js" charset="utf-8"></script>
  <script src="/main.js" charset="utf-8"></script>

这是我的 Nginx 配置

    location / {
            # First attempt to serve request as file, then
            # as directory, then fall back to displaying a 404.
            try_files $uri $uri/ /index.html =404;
            proxy_pass http://localhost:8080;
    }

但我在所有脚本上都遇到了404错误。还有一个奇怪的事情是,这些文件的mime类型被设置为text/HTML

我在这里做错了什么?

我有一个项目,与相同的项目结构,它具有相同的配置,但它对于它可以工作,而在这种情况下却不起作用。


是的,__dirname/home/ubuntu,而 nginx 的 root 路径也设置为 /home/ubuntu - relentless-coder
你能通过 /public/myfile 访问静态文件吗?此外,Nginx 不需要尝试提供 index.html -- 你的 Node 应用程序已经处理了这个。 - ContinuousLoad
我删除了关于Nginx root的评论,因为我记得你正在使用代理传递 -- Nginx不应该尝试提供任何文件。您可以从配置中删除try_filesroot - ContinuousLoad
确定问题是出在 Express 还是 nginx 上会很有帮助。如果您直接向 Express 服务器发出请求,能否访问脚本文件? - skirtle
5个回答

29

您不需要配置Nginx和Express来提供静态文件。两者都能独立完成这项工作,但选择权在您手中。

对于这些示例,我假设您的问题中提供了相同的文件结构。

在这两个配置中,从HTML中加载位于/的文件:

<script src="/main.js"></script> <!-- loads from myapp/public/main.js -->

使用Express作为静态文件服务器,Nginx作为反向代理

Express应用程序

const express = require('express');
const app = express();

app.use(express.static('public')); // notice the absence of `__dirname`, explained later on

// if the request URL doesn't match anything in the 'public' folder,
// it will start searching here next.
app.use(express.static('some_other_folder'));

// from my testing, express will automatically locate index.html if
// it is in a static folder. Declaring a route is not required.

/* 
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
*/

app.listen(8080);

// GET / => index.html
// GET /main.js => main.js
快速提示:在express.static()中使用__dirname是不必要的。实际上,在底层(实际上,在这里的第65行),Express使用本地的Node.jspath.resolve()。从文档中可以看出:

path.resolve()方法将一系列路径或路径片段解析为绝对路径。

实际上,使用path.resolve(__dirname,'public')path.resolve('public')实际上返回相同的结果。我认为你的问题实际上是告诉Nginx同时提供静态文件和代理请求到Express。好的,接下来是我的其他回答。

Nginx配置

server {
  listen 8081; # must be different port from Express
  server_name example.com;
  location / {
    # hand ALL requests back to express
    proxy_pass http://localhost:8080; 
  }
}

Nginx作为静态文件服务器,Express作为API服务器

Nginx配置

server {
  listen 8081;
  server_name example.com;
  location / {
    root /path/to/website/public;
    index index.html;
    try_files $uri $uri/ @express; # instead of 404, proxy back to express using a named location block;
    # source: https://dev59.com/eXNA5IYBdhLWcg3wrf43#15467555
  }
  location @express {
    proxy_pass http://localhost:8080;
  }
}

Express 应用

const express = require('express');
const app = express();

// actually, Nginx has already taken care of static files. You can still define other routes for API functions for example.
app.get('/my/api', (req, res) => {/* query database, etc. */});

app.listen(8080);

希望这能有所帮助!

嘿,谢谢,这起作用了。我使用了第一个配置。你能否指导我一些文档或其他东西,因为我想知道我哪里做错了?比如,不使用 __dirname 如何影响解决方案?以及相同的配置在我的个人项目、公司项目中都有效果,这是为什么呢? - relentless-coder
1
回到这个问题,我认为你最初的问题是Nginx从/home/ubuntu提供服务,而不是从公共目录提供服务。当请求main.js时,它会返回404并停止,从未将任何内容代理到Express。至于您的其他项目,我不能确定,但我想知道它们是否工作,因为您的index.html文件使用src="/public/main.js"加载资源。无论如何,很高兴它能正常工作 :) - ContinuousLoad
2
这是有史以来最棒的回答!我被卡住了整整24个小时,只睡了3个小时。然后我找到了这个答案。感谢@ContinuousLoad。我的所有路由都无法访问,只有主页/能够访问。现在,一切都运转良好。谢谢。 - bozzmob
1
@bozzmob 谢谢,我感到很荣幸。听到长期存在的问题得到解决真是让我开心。我也曾经历过这种情况。此外,我在这个答案中听说了使用try_files中的命名块的技巧,由Chuan Ma提供。如果你(或任何人)给我的回答点赞,请也给那个答案点赞。 - ContinuousLoad
1
@ContinuousLoad 三到四年后,这个答案仍在挽救生命。我认为我从未在其他任何地方看到过如此清晰的握手解释。谢谢! - yen

7
从nginx配置文件中删除try_files指令解决了我的问题。

这是我尝试的所有解决方案中唯一有效的。谢谢,伙计。 - suprxd
那也是我的问题。非常感谢。 - Tim

1
app.use(express.static(path.join(__dirname, '/public')));
app.use(express.static(path.join(__dirname, '/node_modules')));

我认为这两行代码是导致错误的原因。你可以删除第二行并尝试一下吗?也许你的Express应用程序设置了主静态服务文件夹“node_modules”,这就是为什么应用程序无法提供主脚本文件的原因。

此外,我不认为将node_modules目录作为静态文件夹是一个好主意。在你的设置中,“bower”似乎更适合作为JavaScript包管理器。

谢谢


我非常确定express文档中说了这是可以的。首先,静态目录将按顺序搜索文件。关于使用包管理器node_modules的好建议。 - ContinuousLoad
2
推荐不要将node_modules目录作为服务器目录。这是一个非常庞大的目录,可能会暴露出成千上万个文件。更好的方法是只在“public”文件夹中包含您需要的前端库。 - dvsoukup

0
在编程中,将/前添加.以表示相对路径:
<script src="./socket.io/socket.io.js" charset="utf-8"></script>
<script src="./adapter.js" charset="utf-8"></script>
<script src="./main.js" charset="utf-8"></script>

我已经尝试过那个了,我认为它不应该这样工作,因为静态路径设置为__dirname/public,所以./与其不匹配。 - relentless-coder
你能发布浏览器正在请求的完整请求URL吗? - Sua Morales

0
如果您有这个结构:
myApp
  app.js
  -public/
     -js/

app.js 中,你应该这样做:
self.app.use(express.static(path.join(__dirname, 'public')));
staticRoutes.forEach(route=> {
            self.app.use(BASEPATH+route,express.static(path.join(__dirname, 'public')));
        });

其中staticRoutes是一个类似于数组的结构

staticRoutes=[
'services/'
'about/'
];

而在 HTML(开发)中

<script type="text/javascript" src="js/util.js"></script>
<a href="/about">About Us</a>

在生产环境中,最好使用完整的路径和协议。


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