静态文件服务 #

一、静态文件概述 #

1.1 什么是静态文件? #

静态文件是指不需要服务器动态处理的文件,如:

  • CSS样式表
  • JavaScript脚本
  • 图片文件
  • 字体文件
  • HTML文件

1.2 express.static() #

Express内置的静态文件服务中间件:

javascript
app.use(express.static('public'));

二、基本用法 #

2.1 静态文件目录 #

创建public目录:

text
public/
├── css/
│   └── style.css
├── js/
│   └── main.js
├── images/
│   └── logo.png
└── index.html

2.2 配置静态文件服务 #

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

app.use(express.static('public'));

app.listen(3000);

访问:

  • http://localhost:3000/css/style.css
  • http://localhost:3000/js/main.js
  • http://localhost:3000/images/logo.png
  • http://localhost:3000/index.html

2.3 虚拟路径前缀 #

javascript
app.use('/static', express.static('public'));

访问:

  • http://localhost:3000/static/css/style.css
  • http://localhost:3000/static/js/main.js

2.4 多个静态目录 #

javascript
app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

Express按顺序查找文件,先找到的先返回。

三、配置选项 #

3.1 完整配置 #

javascript
app.use('/static', express.static('public', {
    dotfiles: 'ignore',
    etag: true,
    extensions: ['htm', 'html'],
    index: false,
    lastModified: true,
    maxAge: '1d',
    redirect: true,
    setHeaders: (res, path, stat) => {
        res.set('x-timestamp', Date.now());
    }
}));

3.2 配置选项说明 #

选项 说明 默认值
dotfiles 点文件处理 ‘ignore’
etag 生成ETag true
extensions 默认扩展名 false
index 默认文件 ‘index.html’
lastModified Last-Modified头 true
maxAge 缓存时间 0
redirect 目录重定向 true
setHeaders 自定义响应头 -

3.3 点文件处理 #

javascript
app.use(express.static('public', {
    dotfiles: 'allow'
}));
说明
‘allow’ 允许访问
‘deny’ 拒绝访问
‘ignore’ 忽略,当作不存在

3.4 默认扩展名 #

javascript
app.use(express.static('public', {
    extensions: ['html', 'htm']
}));

访问 /about 会自动查找 about.htmlabout.htm

3.5 禁用默认文件 #

javascript
app.use(express.static('public', {
    index: false
}));

3.6 自定义默认文件 #

javascript
app.use(express.static('public', {
    index: ['index.html', 'default.html']
}));

四、缓存控制 #

4.1 设置缓存时间 #

javascript
app.use('/static', express.static('public', {
    maxAge: '1d'
}));

时间格式:

格式 说明
‘1d’ 1天
‘1h’ 1小时
‘1m’ 1分钟
‘1s’ 1秒
3600000 毫秒数

4.2 不同类型不同缓存 #

javascript
app.use('/css', express.static('public/css', {
    maxAge: '7d'
}));

app.use('/js', express.static('public/js', {
    maxAge: '1d'
}));

app.use('/images', express.static('public/images', {
    maxAge: '30d'
}));

4.3 自定义缓存策略 #

javascript
app.use(express.static('public', {
    setHeaders: (res, path) => {
        if (path.endsWith('.html')) {
            res.set('Cache-Control', 'no-cache');
        } else if (path.match(/\.(css|js)$/)) {
            res.set('Cache-Control', 'public, max-age=86400');
        } else if (path.match(/\.(jpg|jpeg|png|gif|ico|svg)$/)) {
            res.set('Cache-Control', 'public, max-age=2592000');
        }
    }
}));

五、安全考虑 #

5.1 限制访问目录 #

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

不要使用:

javascript
app.use(express.static('/'));

5.2 禁止目录列表 #

javascript
app.use(express.static('public', {
    index: false
}));

5.3 自定义响应头 #

javascript
app.use(express.static('public', {
    setHeaders: (res) => {
        res.set('X-Content-Type-Options', 'nosniff');
    }
}));

六、生产环境优化 #

6.1 使用反向代理 #

Nginx配置:

nginx
location /static/ {
    alias /var/www/app/public/;
    expires 30d;
    add_header Cache-Control "public, immutable";
}

6.2 使用CDN #

javascript
app.use((req, res, next) => {
    res.locals.cdnUrl = process.env.CDN_URL || '';
    next();
});

模板中:

html
<link rel="stylesheet" href="<%= cdnUrl %>/css/style.css">

6.3 文件指纹 #

javascript
const assets = {
    'css/style.css': '/css/style.abc123.css',
    'js/main.js': '/js/main.def456.js'
};

app.locals.asset = (path) => assets[path] || path;

模板中:

html
<link rel="stylesheet" href="<%= asset('css/style.css') %>">

七、完整示例 #

7.1 项目结构 #

text
my-app/
├── public/
│   ├── css/
│   │   └── style.css
│   ├── js/
│   │   └── main.js
│   ├── images/
│   │   └── logo.png
│   └── favicon.ico
├── uploads/
│   └── user-photos/
├── views/
│   └── index.ejs
└── app.js

7.2 应用配置 #

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

app.set('view engine', 'ejs');

app.use(express.static(path.join(__dirname, 'public'), {
    maxAge: '1d',
    etag: true,
    lastModified: true,
    setHeaders: (res, filePath) => {
        const ext = path.extname(filePath);
        
        if (['.jpg', '.jpeg', '.png', '.gif', '.ico', '.svg'].includes(ext)) {
            res.set('Cache-Control', 'public, max-age=2592000');
        } else if (['.css', '.js'].includes(ext)) {
            res.set('Cache-Control', 'public, max-age=86400');
        } else if (ext === '.html') {
            res.set('Cache-Control', 'no-cache');
        }
        
        res.set('X-Content-Type-Options', 'nosniff');
    }
}));

app.use('/uploads', express.static(path.join(__dirname, 'uploads'), {
    maxAge: '7d'
}));

app.get('/', (req, res) => {
    res.render('index', { title: '首页' });
});

app.listen(3000, () => {
    console.log('服务器运行在 http://localhost:3000');
});

7.3 模板文件 #

views/index.ejs:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= title %></title>
    <link rel="stylesheet" href="/css/style.css">
    <link rel="icon" href="/favicon.ico">
</head>
<body>
    <header>
        <img src="/images/logo.png" alt="Logo">
        <h1><%= title %></h1>
    </header>
    
    <main>
        <p>欢迎来到Express静态文件示例</p>
    </main>
    
    <script src="/js/main.js"></script>
</body>
</html>

八、总结 #

静态文件服务要点:

配置 说明
express.static(‘public’) 基本配置
express.static(‘public’, options) 带选项配置
‘/static’ 虚拟路径前缀
maxAge 缓存时间
setHeaders 自定义响应头

下一步,让我们学习数据库集成!

最后更新:2026-03-28