自定义SSR项目(非Next.js)中如何从服务器提供favicon.ico?
这是一个未使用Next.js的自定义SSR应用,devServer中运行正常,但执行start脚本启动服务器时,favicon.ico无法正常加载,出现404错误。
项目文件结构
├── assets │ └── favicon.ico ├── client │ ├── components │ │ └── App.tsx │ └── index.tsx ├── config │ ├── tsconfig.client.json │ ├── tsconfig.server.json │ ├── webpack.client.config.ts │ └── webpack.server.config.ts ├── dist │ ├── client │ │ ├── bundle.js │ │ ├── favicon.ico │ │ └── index.html │ ├── server │ │ ├── views | │ │ └── index.hbs │ │ └── server.js ├── node_modules ├── server │ ├── server.ts │ └── views │ └── index.hbs ├── types │ └── custom.d.ts ├── .gitignore ├── package.json └── tsconfig.json
静态资源托管配置
在server.ts中,通过Express托管dist/client目录作为静态资源:
app.use( express.static( path.resolve( __dirname, '../dist/client' ) ) )
Webpack服务器端配置
使用CopyWebpackPlugin复制favicon到dist/client,配置如下:
import path from 'path' import webpack from 'webpack' import nodeExternals from 'webpack-node-externals' import CopyWebpackPlugin from 'copy-webpack-plugin' const config: webpack.Configuration = { entry: './server/server.tsx', target: 'node', externals: [ nodeExternals() ], output: { path: path.resolve( __dirname, '../dist/server' ), filename: 'server.js' }, mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', resolve: { extensions: [ '.tsx', '.ts', '.js', '.jsx' ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new CopyWebpackPlugin( { patterns: [ { from: path.resolve( __dirname, '../server/views' ), to: path.resolve( __dirname, '../dist/server/views' ), }, { from: path.resolve( __dirname, '../assets/favicon.ico' ), to: path.resolve( __dirname, '../dist/client/' ), }, ], } ), ], module: { rules: [ { test: /\.(tsx?|js)$/, exclude: /node_modules/, use: [ { loader: 'ts-loader', }, ], }, ] } } export default config
错误信息
浏览器控制台报错:
GET http://localhost:3000/favicon.ico 404 (Not Found)

尝试将CopyWebpackPlugin移到客户端Webpack配置中时,会出现blob错误,情况异常。
1. 确保构建流程完整
检查start脚本是否先执行了server端构建操作。如果直接运行dist/server/server.js但未重新构建,favicon可能没被复制到dist/client目录。修改package.json中的脚本,确保构建前置:
"scripts": { "build:server": "webpack --config config/webpack.server.config.ts", "build:client": "webpack --config config/webpack.client.config.ts", "build": "npm run build:client && npm run build:server", "start": "npm run build && node dist/server/server.js" }
2. 修正Express静态资源路径
__dirname在编译后的server.js中指向dist/server,原路径写法本身正确,但可以换成更稳妥的绝对路径写法,避免工作目录差异导致的问题:
const rootDir = path.resolve(__dirname, '../../'); app.use(express.static(path.join(rootDir, 'dist/client')));
或简化为相对路径(Express会自动解析为当前进程工作目录的相对路径):
app.use(express.static('../dist/client'));
3. 修复客户端Webpack配置的blob错误
若将CopyWebpackPlugin移到客户端配置后出现blob错误,检查客户端Webpack的output.publicPath是否为'/',同时给CopyWebpackPlugin明确指定文件名:
new CopyWebpackPlugin({ patterns: [ { from: './assets/favicon.ico', to: './', filename: 'favicon.ico' } ] })
确保客户端Webpack的output.path设置为dist/client,保证文件复制到正确目录。
4. 验证文件复制路径
启动服务前手动检查dist/client/favicon.ico是否存在。若不存在,调整CopyWebpackPlugin的路径写法,改用相对项目根目录的路径减少出错概率:
// 替换原from/to路径 { from: './assets/favicon.ico', to: '../dist/client/' }
内容的提问来源于stack exchange,提问作者Brent Anderson

