前端开发必会的Nginx知识及结合Docker的项目部署实战
前端开发必会的Nginx知识及结合Docker的项目部署实战
起因
事情的起因来自一封邮件,年前上线了一个项目官网,在过年期间,突然收到了一封来自安全部门邮件,说上线的官网存在以上安全风险,让尽快处理。现在看来解决这个问题很容易,只需要在Nginx上的server 块中配置 server_name为备案域名,再设置一个默认的server快,匹配所有未明确指定 server_name 的请求并返回403就OK,但是当时对Nginx并不熟悉,导致走了些弯路。所以这篇文章主要是前端在项目部署中,针对常见的Nginx功能的介绍。基于目前前端项目部署方式大多采用Docker的CI/CD工作流来进行,所以也会涉及到Docker以及常见项目部署配置的相关知识。
Nginx介绍
Nginx是由伊戈尔·赛索耶夫在2002年创建的,最初是为了解决C10K问题,也就是同时处理上万个并发连接的问题。Apache服务器当时在处理大量并发时效率不高,所以Nginx采用了事件驱动的异步架构,性能更好,资源消耗更低。后来在2004年公开发布,凭借其轻量级、事件驱动、异步非阻塞的架构,迅速成为高性能 Web 服务器和反向代理的热门选择。
前端常用的Nginx功能
我们将从以下方面介绍前端开发与项目部署中常见的Nginx相关功能,主要涉及:基于Docker的Nginx安装、配置文件、静态资源托管、反向代理、负载均衡、HTTPS 配置(SSL 证书)等
基于Docker的Nginx安装
基于Mac电脑,假设你已经安装好看Docker,通过下面的一行命令就可以安装基于Docker容器的Nginx
docker run -d -p 80:80 --name my-nginx nginx
这段命令用于运行一个 Nginx Docker 容器,并将其端口映射到主机的端口。
docker run:这是 Docker 命令,用于创建并运行一个新的容器。-d:这是一个选项,表示以分离模式(detached mode)运行容器。容器将在后台运行,而不会占用当前终端。-p 80:80:这是一个端口映射选项,格式为 主机端口:容器端口, 80:80 表示将主机的端口 80 映射到容器的端口 80。这样,访问主机的端口 80 时,实际上是在访问容器的端口 80。--name my-nginx:这是一个选项,用于为容器指定一个名称。在这里,容器的名称被指定为 my-nginx。
这样你可以通过这个名称来管理和引用这个容器,而不需要使用容器的 ID。nginx:这是要运行的镜像名称。在这里,使用的是官方的 Nginx 镜像。
Docker 会从 Docker Hub 拉取这个镜像(如果本地没有的话),并基于这个镜像创建并运行一个新的容器。
当我们在浏览器访问: http://localhost,若看到欢迎页即安装成功。
配置文件
当我们在主机上运行my-nginx容器后,可以通过命令:
docker exec -it my-nginx /bin/sh
来进入到容器中,查看和修改nginx的相关配置,nginx的主配置文件为: /etc/nginx/nginx.conf,执行cat nginx.conf查看文件为:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
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;
include /etc/nginx/sites-enabled/*;
}
这是 Nginx 的核心配置文件,定义了全局配置(如 Worker 进程数、日志格式、事件模型等)。通常不直接在这里配置具体的站点,而是通过 include 指令引入其他配置文件。我们一般不会修改这里的主配置文件,而是通过下面的/etc/nginx/conf.d/ 或 /etc/nginx/sites-available/来进行站点配置文件的配置。
站点配置目录: /etc/nginx/conf.d/ 或 /etc/nginx/sites-available/为什么有两个目录?
/etc/nginx/conf.d/:这是一个简单的配置目录,通常用于存放单个站点的配置文件。每个文件通常对应一个站点或服务,文件名以 .conf 结尾。适合小型项目或简单场景。也是我们前端开发最常用的目录。
/etc/nginx/sites-available/ 和 /etc/nginx/sites-enabled/:这是一个更灵活的配置管理方式,适合复杂的多站点场景。
/sites-available/: 存放所有站点的配置文件(相当于“配置仓库”),/sites-enabled/: 存放当前启用的站点配置,这种方式可以方便地启用或禁用站点,而无需删除配置文件。
执行cat /etc/nginx/conf.d/default.conf 查看默认的配置文件
server {
listen 80;
listen [::]:80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
#access_log /var/log/nginx/host.access.log main;
location / {
try_files $uri $uri/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
我们部署前端项目,通过Nginx实现的相关功能,都是在这个文件中进行相关字段的配置而实现的。
server: 用于定义一个虚拟主机(Virtual Host),它决定了 Nginx 如何处理来自不同域名或 IP 的请求。listen 80:监听 80 端口(HTTP 默认端口)。server_name localhost:指定匹配的域名,用于虚拟主机识别,当请求的Host头匹配localhost时,该服务器块会处理请求。root /usr/share/nginx/html;:指定服务器的根目录,用于查找静态文件。index index.html:设置默认的首页文件: 用于指定默认的索引文件,当请求的路径是目录时,Nginx 会尝试返回index.html或index.htmlocation块: 根据请求的 URL 路径定义不同的处理规则。error_page:定义错误页面的处理规则。当服务器返回 500、502、503 或 504 错误时,返回/50x.htmllocation = /50x.html: 精确匹配/50x.html路径,并指定其根目录, 也就是/50x.html文件会从/usr/share/nginx/html目录中查找
根据这些字段的配置就可以解决上面邮件中提到的问题了。只需要在Nginx上的server 块中配置 server_name为备案域名,再设置一个默认的server快,匹配所有未明确指定 server_name 的请求并返回403就可以了。
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# 默认 server 块,拒绝非法请求
server {
listen 80 default_server;
server_name _;
return 403;
}
上面我们定义了两个server块,只有当来自host为localhost的请求才会匹配第一个server块,第二个是默认的server,匹配任意的host,我们直接返回403状态码。
注意:server_name 是必须的吗?不是必须的,但强烈建议配置。如果 server_name 未指定,Nginx 可能会匹配默认服务器,导致:
潜在的安全问题:恶意用户可以通过 IP 直接访问 你的 Nginx 服务器,可能会暴露本应受限的服务。
错误的路由: 多个站点可能会被错误地解析到默认的 server 块,导致访问到错误的网站或泄露敏感信息。
静态资源托管
Nginx 采用事件驱动架构,能够高效处理大量并发请求,适合托管静态资源(如 HTML、CSS、JavaScript、图片等),最长见的方式是托管一个单页应用(SPA)。我们将前端打包好的dist文件直接拷贝到Nginx的静态资源目录,并进行Nginx的相关配置就可以实现SPA应用的部署。
- 在启动的
my-nginxdocker容器中执行
docker cp ./Desktop/dist my-nginx:/usr/share/nginx/
将本地的dist文件拷贝到容器的/usr/share/nginx/目录下,你可以通过docker exec -it my-nginx /bin/bash 进入容器,查看是否拷贝成功。
在本地修改
default.conf配置文件,然后将其拷贝到Nginx主配置文件下进行覆盖server { listen 80; listen [::]:80; server_name localhost; #access_log /var/log/nginx/host.access.log main; # 这个静态资源目录进行了修改 root /usr/share/nginx/dist; index index.html index.htm; location / { try_files $uri $uri/ /index.html; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # 设置缓存时间 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public"; } } # 默认 server 块,拒绝非法请求 server { listen 80 default_server; server_name _; return 403; }注意将原来的静态资源目录修改为
root /usr/share/nginx/dist;这个是刚才我们拷贝的打包资源的目录。执行覆盖命令
docker cp ./Desktop/default.conf my-nginx:/etc/nginx/conf.d/default.conf执行完成后,重新启动 Docker 容器:
docker restart my-nginx, 浏览器访问http://localhost/就可以看到项目部署成功了。try_files指令的设置为:**try_files $uri $uri/ /index.html;** 当访问指定路径时,$uri:尝试查找请求的文件,$uri/:尝试查找请求的目录,/index.html:如果未找到文件或目录,则返回index.html。SPA 只有一个 index.html,前端路由由 JavaScript 处理,所有它最终都返回 index.html,由前端来解析路径。这适用于Vue Router、React Router、Angular Router的 history 模式,避免 404 的问题。
反向代理与负载均衡
为什么前端要了解Nginx的反向代理?
- 解决跨域问题:对于前后端分离的项目比如:如前端部署在
http://frontend.com,后端在http://api.example.com),通过 Nginx 将前后端请求统一到同一域名下(如前端http://example.com,APIhttp://example.com/api),浏览器认为这是同源请求,避免跨域限制。 - 隐藏后端服务:另一方面可以避免直接暴露后端服务的 IP 或端口存在安全风险,通过反向代理方案,后端服务仅对 Nginx 可见,外部请求通过 Nginx 转发,隐藏真实后端地址。
- 高并发场景下的负载均衡:高并发场景下,单台后端服务器可能无法处理所有请求。通过反向代理,Nginx 将流量分发到多个后端实例,提升系统吞吐量和容错能力。
如何实现反向代理:
还是基于上面的例子,假如我们有个后端的服务地址为:http://example.com/api, 而前端是本地的localhost,可以通过如下配置实现反向代理:
server {
listen 80;
server_name localhost;
# 前端静态资源
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html; # 支持 SPA 路由
}
# 反向代理到后端 API
location /api/ {
proxy_pass http://example.com; # 后端服务地址
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
proxy_pass:将匹配的请求转发到后端服务。这里需要注意location后面的路径与proxy_pass 的匹配规则,当前的设置会匹配以
/api/开头的请求(例如/api/user、/api/data),**proxy_pass**末尾不带/,则会将客户端请求的 URI 完整追加到目标地址后,实际的代理路径为:http://example.com/api/user 与http://example.com/api/data, 若
proxy_pass包含路径且以/结尾,Nginx 会替换location匹配部分,实际路径为:http://example.com/user,http://example.com/dataproxy_set_header:传递客户端真实 IP 和域名信息到服务端。方便后端记录和分析。后端可以通过 X-Real-IP 读取用户 IP,用于日志记录、IP 限制、用户追踪等操作。
负载均衡
要配置 Nginx 实现负载均衡,你需要在 Nginx 配置文件中定义一个 upstream 块,并在服务器块中使用 proxy_pass 指令将请求转发到定义的上游服务器组。以下是一个示例配置,展示了如何配置 Nginx 实现负载均衡:
upstream backend {
server backend1.example.com weight=3;
server backend2.example.com;
server backend3.example.com;
}
server {
listen 80;
server_name example.com;
# 反向代理到上游服务器组
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
- 上游服务器组:使用 upstream 指令定义一个上游服务器组,包含多个后端服务器。
- 负载均衡策略:默认情况下,Nginx 使用轮询(round-robin)策略进行负载均衡。你可以通过设置权重(weight)来调整服务器的负载分配,权重用于控制请求的分配比例:
- 权重越高:分配到的请求越多。
- 默认权重:如果未指定权重,默认为 1。
- 反向代理:在服务器块中使用 proxy_pass 指令将请求转发到上游服务器组。
HTTPS 配置(SSL 证书)
通过 Nginx 配置网站的 HTTPS 协议访问是一个常见且重要的任务,首先我们准备好SSL/TLS 证书,一般是从权威证书颁发机构(CA)来获取,证书通常包括以下两个文件:
- 证书文件(如
example.com.crt) - 私钥文件(如
example.com.key)
将证书文件拷贝到Nginx服务的目录下一般放在:/etc/nginx/ssl/目录下
进行Nginx的配置:
server {
listen 443 ssl;
server_name example.com;
# SSL 证书和私钥路径
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# SSL 配置
ssl_protocols TLSv1.2 TLSv1.3; # 推荐使用 TLS 1.2 和 1.3
ssl_ciphers HIGH:!aNULL:!MD5; # 推荐使用安全的加密套件
ssl_prefer_server_ciphers on;
root /usr/share/nginx/html;
index index.html index.htm;
# 处理请求
location / {
try_files $uri $uri/ =404;
}
}
监听443端口,并且通过certificate与ssl_certificate_key设置对应的证书与key的路径
为了确保所有访问都通过 HTTPS,可以配置 HTTP 请求自动重定向到 HTTPS:
server {
listen 80;
server_name example.com;
# 重定向所有 HTTP 请求到 HTTPS
return 301 https://$host$request_uri;
}
以上就是前端常见的Nginx 的配置方式,下面我们结合docker来实际部署我们项目。
部署SPA项目
假设你已经打包好了你的项目,并且生成了dist文件,下面是文件目录结构:
my-nginx-spa/
├─ dist/
│ ├─ index.html
│ └─ styles.css
├─ default.conf
└─ Dockerfile
default.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# 默认 server 块,拒绝非法请求
server {
listen 80 default_server;
server_name _;
return 403;
}
Dockerfile
# 使用官方 Nginx 镜像作为基础镜像
FROM nginx:1.25-alpine
# 将本地的 default.conf 复制到容器中
COPY default.conf /etc/nginx/conf.d/
#将本地的 dist 目录复制到容器中的 Nginx 默认静态文件目录
COPY dist /usr/share/nginx/dist
# 暴露 80 端口
EXPOSE 80
# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]
执行docker构建命令打包镜像
docker build -t nginx-spa-image .
基于镜像运行docker容器
docker run --name nginx-spa-container -d -p 80:80 nginx-spa-image
运行后浏览器访问: http://localhost 就可以看到我们部署的项目了。
部署nuxtjs的SSR项目
相比SPA项目,SSR项目的部署要麻烦一点,因为不仅需要一个Nginx服务来托管我们的静态资源,还需要一个Node服务来进行服务端的渲染逻辑。
这里介绍两种部署方案:由于需要两个容器来分别进行node服务与Nginx服务的启动,我们需要使用docker-compose 来进行容器的管理与编排,这就是方案1的思路,当然也可以不使用docker-compose,而是将node与Nginx服务放在一个容器中来简化部署,这就是方案2的部署思路。
方案1:
my-nginx-ssr/
├─ .output/
│ ├─ public/
│ └─ server/
├─ node/
│ └─ Dockerfile
├─ nginx/
│ └─ nginx.conf
├─ docker-compose.yml
└─ package.json
nginx.conf
server {
listen 80;
server_name localhost;
# 静态资源处理(由 Nginx 直接提供)
location /_nuxt/ {
alias /usr/share/nginx/html/_nuxt/; # 静态资源目录(需与 Docker 挂载路径一致)
expires 1d;
add_header Cache-Control "public";
try_files $uri $uri/ =404;
}
# 反向代理到 Node 服务(处理 SSR 请求)
location / {
proxy_pass http://node-service:3000; # 使用 Docker 服务名通信
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# 默认 server 块,拒绝非法请求
server {
listen 80 default_server;
server_name _;
return 403;
}
Dockerfile
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 1. 复制依赖清单文件(利用 Docker 层缓存加速构建)
COPY package*.json ./
# 2. 安装生产依赖
RUN npm install
# 3. 复制构建产物(.output 目录)
COPY .output/ ./.output/
# 4. 暴露服务端口(与 Nuxt 配置的端口一致,默认 3000)
EXPOSE 3000
# 5. 启动命令(直接运行 Node 服务)
CMD ["node", ".output/server/index.mjs"]
docker-compose.yml
version: '3.8'
services:
# Node.js 服务
node-service:
build:
context: . # 上下文路径为项目根目录
dockerfile: node/Dockerfile # 指定 Dockerfile 路径
container_name: nuxt-node
environment:
- NODE_ENV=production
networks:
- nuxt-network
# Nginx 服务
nginx:
image: nginx:alpine # 使用官方 Nginx Alpine 镜像
container_name: nuxt-nginx
ports:
- "80:80" # 映射宿主机 80 端口到容器 80 端口
volumes:
# 挂载 Nginx 配置文件(覆盖默认配置)
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
# 挂载静态资源目录(Nginx 直接提供 _nuxt 内容)
- ./.output/public/_nuxt:/usr/share/nginx/html/_nuxt
depends_on:
- node-service # 确保 Node 服务先启动
networks:
- nuxt-network
# 定义共享网络(容器间通过服务名通信)
networks:
nuxt-network:
driver: bridge
为了减轻node服务的压力,实现静态资源的高效访问,这里利用了Nginx的静态资源托管的能力,将Nuxt的静态资源通过docker-compose的volumes来挂载到Nginx对应的目录上,静态资源处理由 Nginx 直接提供。
需要运行docker-compose命令来进行镜像的打包与容器的启动
#构建镜像并启动容器(后台运行)
docker-compose up -d --build
方案2:
my-nginx-node-ssr/
├─ .output/
│ ├─ public/
│ └─ server/
├─ node/
│ └─ Dockerfile
├─ nginx/
│ └─ nginx.conf
└─ package.json
不使用docker-compose,而是将node与Nginx服务放在一个容器中来简化部署的方案:
nginx.conf
server {
listen 80;
server_name localhost;
location /_nuxt/ {
alias /usr/share/nginx/html/_nuxt/;
expires 1d;
add_header Cache-Control "public";
try_files $uri $uri/ =404;
}
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80 default_server;
server_name _;
return 403;
}
Dockerfile
# 使用 Node.js 官方镜像(Alpine 版本体积更小)
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 1. 复制依赖清单文件(利用 Docker 层缓存加速构建)
COPY package*.json ./
# 2. 安装生产依赖
RUN npm install
# 3. 复制构建产物(.output 目录)
COPY .output/ ./.output/
# 4. 安装 Nginx
RUN apk add --no-cache nginx
# 5. 复制 Nginx 配置文件
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
# 6. 确保 Nginx 主配置文件包含必要的基本结构
RUN echo "user nginx; \
worker_processes auto; \
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; \
keepalive_timeout 65; \
include /etc/nginx/conf.d/*.conf; \
}" > /etc/nginx/nginx.conf
# 7. 检查 Nginx 配置语法
RUN nginx -t
# 8. 复制静态资源到 Nginx 目录
COPY .output/public/_nuxt /usr/share/nginx/html/_nuxt
# 9. 暴露端口
EXPOSE 80
# 10. 启动命令
CMD sh -c "npm run start & nginx -g 'daemon off;'"
同样的先运行docker镜像构建命令然后运行docker命令
# 构建镜像
docker build -t nginx-node-image -f node/Dockerfile .
# 基于镜像启动容器
docker run -d -p 80:80 --name nginx-node-container nginx-node-image
总结
通过掌握上述Nginx核心功能及Docker化部署技巧,前端开发者可独立完成从本地开发到生产上线的全链路工作,构建高效、安全、易维护的Web应用。


