纸飞机的信笺
博客Awesome开源Demos制品库

Nginx 配置模板与解读

  1. 禁用 IP 访问
  2. 开启 HTTP2
  3. 跳转 https
  4. 自动添加或去除 www. 前缀
  5. SPA 的配置
  6. SPA 禁用 html 缓存
  7. gzip 压缩
  8. 禁用 SourceMap
  9. WebSocket 配置
  10. location 路由的几种配置的区别
  11. root 和 alias 的区别
  12. 图片防盗链
  13. 验证 UA
  14. robots.txt 的配置
  15. proxy_pass 的 proxy_set_ 系列配置
  16. proxy_pass 连接 CNAME 的外部服务
  17. 防超时
  18. 身份认证
  19. 大文件传输

Nginx 配置模板与解读

2022年 6月 28日DevOps#DevOps#DOC#Cheatsheet#Scaffold留言: ...

持续更新,记录常用的 Nginx 配置模板,记录容易混淆和难以理解的配置项的含义和用法。

推荐访问:

  • 在线 Nginx 配置;
  • Nginx 中文文档;
  • 强烈推荐:中文 Nginx 入门教程。

禁用 IP 访问#

以下示例中,阻断直接 IP 访问:

server {
  server_name _;
  listen 80 default_server;
  listen [::]:80 default_server;
 
  return 444;
}

这里出现了:

  • _ 这个主机名,它表示:接受未被任何其他 server_name 匹配到的主机名;
  • default_server 这个端口监听的关键字,它表示这个端口监听默认的服务器主机,也就是没有匹配到任何 server 时,使用这个监听来处理请求;
  • 444 状态码,它不是一个标准 HTTP 状态码,仅用于 Nginx 内部,表示拒绝连接,浏览器显示为 “XXX 未发送任何数据”,可以避免暴露这个 IP 是一台 Web 服务器。

开启 HTTP2#

新版本:

server {
  # ...
  listen 80;
  listen 443 ssl;
  http2 on;
}

之前的版本:

server {
  # ...
  listen 80 http2;
  listen 443 ssl http2;
}

写法和 HTTP1.1 的写法完全不同。

跳转 https#

server {
  # ...
  listen 80;
  listen [::]:80;
 
  return 308 https://$host$request_uri;
}

请注意,这里的没有使用 301 永久重定向状态码,而是使用 308 状态码。好处是:

  • 301 状态码兼容性更好,但它会始终把请求改为 GET,也就是说 API 请求如果遇到 301 跳转,请求方法被改变,会导致出错;
  • 308 则表示永久重定向的同时,还保持请求的方法,目前浏览器已经支持了,但是 Postman 等工具支持的不太好。

自动添加或去除 www. 前缀#

去除 www. 前缀:

server {
  server_name www.your-domain.com;
  listen 443 ssl;
  listen [::]:443 ssl;
 
  ssl_certificate /path-to/fullchain.pem;
  ssl_certificate_key /path-to/key.pem;
 
  return 301 https://your-domain.com$request_uri;
}

添加 www. 前缀:

server {
  server_name your-domain.com;
  listen 443 ssl;
  listen [::]:443 ssl;
 
  ssl_certificate /path-to/fullchain.pem;
  ssl_certificate_key /path-to/key.pem;
 
  return 301 https://www.your-domain.com$request_uri;
}

SPA 的配置#

location /subpath/ {
  alias /path-to-index-html/;
  try_files $uri $uri/ $uri/index.html;
}

这里给的示例是网站部署在子路径的写法,如果部署在根目录,直接写成 location / 即可。

记得 location 和 alias 尾缀的斜杠 /,都不要省略,否则可能会有 安全问题。

SPA 禁用 html 缓存#

location /subpath/ {
  alias /path-to-index-html/;
  try_files $uri @website-name;
  index index.html;
}
 
# 下面这条规则只对 index.html 生效
location @website-name {
  # ↓ 必须用 root,命名的 location 中不能使用 alias
  root /path-to-index-html/;
  try_files /index.html =404;
 
  # 下面就是禁用缓存相关的配置,本段参考了知乎、腾讯云的响应体
  add_header Cache-Control "private, no-cache, no-store, max-age=0";
  add_header Pragma "no-cache";
  add_header Expires "0";
}

其中 @website-name 可以自定义,在两段配置中保持一致即可。

gzip 压缩#

开启 gzip 压缩可以显著减小传输的流量,对小带宽服务器而言非常合适,带来的副作用是服务器 CPU 占用提高。

使用这种方式开启:

http {
  gzip on;
  gzip_comp_level 5;
  gzip_types text/plain
    text/css
    text/javascript
    application/javascript
    application/xml
    application/json;
}

Nginx 支持对 gzip 压缩进行详细的配置,例如根据 UA 判断压缩与否、小于多大的文件不压缩、压缩缓冲区等,本文不再赘述。

禁用 SourceMap#

暴露 SourceMap 是极度危险的行为,其他人可以很容易的获得网站源码。

如果你使用 create-react-app,推荐在打包时添加 GENERATE_SOURCEMAP=false 环境变量(或者写入 .env 文件中) ,这样就不会生成 SourceMap 了。或者是通过 CI/CD 配置,拷贝文件时跳过 .map 文件。

如果必须使用 Nginx 配置,可以这样配置:

location ~* \.map$ {
  deny all;
  return 404;
}

如果希望在公司的 IP 地址可以访问 .map 文件,来方便调试,可改为:

location ~* \.map$ {
  allow 10.20.30.40;
  deny all;
}

把这里的数字改成公司的 IP 地址即可。

WebSocket 配置#

location / {
  # ...
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
}

location 路由的几种配置的区别#

修饰符: 对比以下配置:

location /subpath { }
location = /subpath { }  # 需精确匹配 /subpath
location ~ /subpath { }  # 正则匹配,区分大小写
location ~* /subpath { } # 正则匹配,不区分大小写
location ^~ /subpath { } # 优先正则匹配,匹配到后立即停止向后搜索规则

路径: 纯前端部署时,可以不管是否有尾缀斜杠 /; 但使用 proxy_pass 接入后端服务时,需注意:如果有尾缀斜杠 /,传入服务的流量路径会依据 location 中的路径进行截断。

例如某网站的后端 API 地址前缀为 /api,而后端服务直接接受不带前缀的 API 的路径,则需要配置为:

location /api/ {
  proxy_pass service-host;
}

这里 /api/ 尾缀的斜杠不能去掉,否则传入后端时,路径会以 /api 前缀开头。

root 和 alias 的区别#

网上很多文章说不清楚。实际上,Nginx 配置关键字 root 和 alias 的区别在于:

  • root 指定根目录后,访问资源的 path 时基于 root + location;
  • alias 指定根目录后,访问资源的 path 时基于 alias,“吃掉” 了 location 部分。

所以,只要你的网站不是部署在根目录下,那基本必须用到 alias,因为访问网站资源文件时要把网站的 “baseURL” 给去掉。

如果还理解不了,这里举个例子,假设 Nginx 配置:

location /path-a/ {
  root /home/root/web-a/;
}
 
location /path-b/ {
  alias /home/root/web-b/;
}

访问 /path-a/images/a.png 时,实际在访问 /home/root/web-a/path-a/images/a.png; 访问 /path-b/images/b.png 时,实际在访问 /home/root/web-b/images/b.png。

可以看出区别,就是 location 的部分被 alias 给 “吃掉” 了。

图片防盗链#

本段转载自 xuexb 的中文 Nginx 教程。

location ~* \.(gif|jpg|jpeg|png|bmp|webp)$ {
  valid_referers none blocked *.your-domain.com server_names;
 
  # 如果验证不通过则返回 403
  if ($invalid_referer) {
    return 403;
  }
}

验证 UA#

location / {
  if ($http_user_agent !~* "(Mozilla|Chrome)") {
    return 404;
  }
 
  # ...
}

这里提供一个避免简单爬虫的配置示例:

if ($http_user_agent ~* "(Go-http-client|node-fetch|python-requests|Python-urllib|Custom-AsyncHttpClient|BotPoke|^$)"){
  return 403;
}

注:请根据实际情况充分测试后使用,切勿直接照抄。 例如:Go-http-client 是 Drone CI 用来进行 OAuth 认证的请求工具,屏蔽后 Drone CI 无法登录; 而 node-fetch 是 Joplin 进行同步的请求工具,屏蔽后 Joplin 无法连接服务器。

robots.txt 的配置#

location /robots.txt {
  alias /path-to-your/robots.txt;
}

proxy_pass 的 proxy_set_ 系列配置#

给后端服务配置 Nginx 时,通常会配置以下几条:

location / {
  # ...
  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;
}

第一条写回真实的 Host 头,后面三条分别添加三个请求头,用于透传代理前这些请求的客户端 IP、途经 IP、协议等信息。

proxy_pass 连接 CNAME 的外部服务#

很多云服务供应商提供的 “云主机”、“云函数” 不会给用户分配公网 IP,而是只能使用 CNAME 来绑定自己的域名; 此时,通过 proxy_pass 连接,要带上自身绑定域名的 Host,还需要带上 SNI,分别用以下两行配置实现:

proxy_set_header Host $proxy_host; # 这个不是 $host 了
proxy_ssl_server_name on;

注意这里是 $proxy_host 而不再是 $host。

以下是原理简介:

因为有很多个域名都会 CNAME 到云服务供应商(以下简称 “厂商”)的某个服务器上,供应商必须区分用户,有两种方式:以前是基于 Host,现在更多的是基于 SNI。

SNI 是 TLS 通讯协议的扩展,用于在 TLS (可以理解成 HTTPS 的加密协议)握手阶段,把主机名发送给服务器。 所以说我们虽然使用了 HTTPS,请求全部加密了,但是网络设施还是知道我们访问的域名,这就是因为 SNI 会让 HTTPS 的目标域名明文可见。

厂商如果想拿到 Host,这需要完成 TLS 握手、HTTP 请求解包等,实际上已经进入厂商服务器并完成一系列流程了; 而现在的很多厂商例如 Cloudflare,会提供全球范围的就近路由等服务,要尽可能早的完成流量分配,所以直接基于 SNI 拿到域名确定用户是谁并尽早分配区域,这才是现在最流行的做法。

所以 Nginx 通过 proxy_pass 连接时,尽量做到既让域名也就是 Host 对得上,又让 SNI 对得上,使用上面两条配置便可一次性解决,也适用于各类厂商。

防超时#

# 如果服务收到请求要很久才能返回响应,那么调大下面这个值
proxy_read_timeout 90;
 
# 下面这两个不常用
# 将请求发送给服务的超时时间
proxy_send_timeout 90;
 
#与服务建立 TCP 连接的超时时间
proxy_connect_timeout 90;

身份认证#

配置 nginx:

location / {
  # 这个字符串是提示语,可以自行修改
  auth_basic "Restricted Area";
  auth_basic_user_file /path/to/your/app.htpasswd;
}

此时需要提供一个 .htpasswd 的用户名密码文件,这个文件需要使用 httpd 指令来生成。 此处给出使用 Docker 来生成的指令:

docker run --rm --entrypoint htpasswd httpd -Bbn <用户名> <密码>

运行后会输出用户名和哈希过的密码,可以将它追加到 .htpasswd 文件中。 这段指令执行后,会自动删除 Docker 容器,没有其他副作用。

大文件传输#

允许上传大文件:

# 上传文件大小限制 100MB
client_max_body_size 100m;
 
# 或,不限上传文件大小
client_max_body_size 0;

默认情况下,Nginx 会使用缓冲区(内存)和临时文件(硬盘)来暂存用户上传的文件,然后发送给上游; 你可以调整缓冲区的大小、临时文件的目录:

# 缓冲区大小
client_body_buffer_size 512k;
 
# 临时文件位置,后面的 1 2 表示目录层次
client_body_temp_path /path-to-temp 1 2;

或者,你可以直接禁用 Nginx 的文件缓冲区:

proxy_request_bufferings off;

如果你的上游是 Jellyfin、NAS 等,需要支持分块下载、断点续传、视频播放,建议配置:

proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;

修订记录

  • 2026年 5月 6日
    feat(blog): 博客支持日期、类别、标签分组;更新博客文章页面
  • 2025年 12月 2日旧版 Hexo 博客
    feat(article): 《Nginx》新增大文件传输内容
  • 2025年 8月 13日旧版 Hexo 博客
    feat(article): 《Nginx》增补 CNAME 主机内容
  • 2024年 12月 23日旧版 Hexo 博客
    feat(article): 《Nginx》更新,更新 444 状态码,更新 gzip 压缩,更新 index.html 禁用缓存配置
  • 2024年 6月 22日旧版 Hexo 博客
    feat(article): 《Nginx 配置》勘误
查看全部修订

留言