server 块与虚拟主机

上一篇我们看过 nginx.conf 的整体结构,知道 http 块里会嵌套若干个 server 块。这一章我们深入 server 块——它代表一个虚拟主机,是 Nginx 区分"不同站点"的核心机制。一台机器通过 server 块的巧妙组合,可以同时服务几十、几百个域名。

1. 一个最基础的 server 块

先看一个能跑起来的最小配置,理解每个指令的作用:

# 最基础的 server 块
server {
    listen 80;                  # 监听 80 端口(HTTP 默认)
    server_name example.com;    # 匹配的域名
    root /var/www/example;      # 网站根目录
    index index.html;           # 默认首页文件名

    location / {
        # 这里暂时留空,root 和 index 已在 server 层定义
        try_files $uri $uri/ =404;
    }
}

这个 server 监听 80 端口,匹配域名 example.com,把请求映射到 /var/www/example 目录。这一节我们先理解四个核心指令:listenserver_namerootindex

2. listen:监听哪个端口

listen 决定 Nginx 在哪个网络端口/IP 上等连接。常见写法:

# listen 指令的几种写法
server {
    listen 80;                       # 监听所有网卡的 80 端口
    listen 127.0.0.1:80;             # 只监听 127.0.0.1(本机访问)
    listen 8080;                     # 监听非标准端口
    listen 443 ssl;                  # 监听 443,并启用 SSL(HTTPS)
    listen 443 ssl http2;            # 同时启用 HTTP/2
    listen [::]:80;                  # IPv6 监听
    listen 80 default_server;        # 标记为"默认 server"(兜底)
}

# default_server 的作用:当 Host 头不匹配任何 server_name 时,
# 请求会被发到 default_server 标记的那个 server

default_server 是个高频面试题:当请求的 Host 头不匹配任何 server_name 时,请求会被发到标记为 default_server 的那个 server。这个机制常用来拦截"恶意域名指向"——给 default_server 返回 444(无响应关闭连接)。

3. server_name:匹配哪个域名

server_name 是 Nginx 区分多站点的核心——通过 HTTP 请求头里的 Host 字段,Nginx 把请求路由到对应的 server 块:

# server_name 支持多种匹配形式
server {
    listen 80;
    server_name example.com;                 # 精确匹配
}

server {
    listen 80;
    server_name *.example.com;               # 通配符(匹配 www.example.com、api.example.com 等)
}

server {
    listen 80;
    server_name www.example.org ~^www\d+\.example\.org$;   # 正则(前面加 ~)
}

server {
    listen 80 default_server;
    server_name _;                           # 下划线是常用"无效域名"占位符,配合 default_server 兜底
}

# 匹配优先级(高到低):
#   1. 精确匹配 example.com
#   2. 前缀通配 *.example.com
#   3. 后缀通配 www.example.*
#   4. 正则匹配 ~^...$
#   5. default_server 兜底

记住优先级口诀:精确 → 通配 → 正则 → 兜底

几个常见坑:

4. 三种虚拟主机类型

"虚拟主机"(Virtual Host)就是一台物理机伪装成多台服务器的技术。Nginx 支持三种区分方式:

# === 基于域名的虚拟主机(最常用)===
# 一台机器一个 IP,靠 Host 头分发到不同站点
server {
    listen 80;
    server_name siteA.com;
    root /var/www/siteA;
}
server {
    listen 80;
    server_name siteB.com;
    root /var/www/siteB;
}

# === 基于端口的虚拟主机 ===
# 同一域名,不同端口
server {
    listen 8000;
    server_name example.com;
    root /var/www/app1;
}
server {
    listen 8001;
    server_name example.com;
    root /var/www/app2;
}

# === 基于 IP 的虚拟主机 ===
# 服务器有多个网卡/IP
server {
    listen 192.168.1.10:80;
    server_name siteA.com;
}
server {
    listen 192.168.1.11:80;
    server_name siteB.com;
}

实际生产中,基于域名的虚拟主机占 99%——因为它不需要多 IP,也不需要让用户输入端口号。

5. root 与 index

这两个指令决定"请求路径如何映射到磁盘文件":

# root 指令:把 URI 拼到 root 后面查找文件
server {
    root /var/www;       # server 层定义 root

    location /images/ {
        # root 会被继承:访问 /images/cat.png → /var/www/images/cat.png
    }

    location /static/ {
        root /data;      # 覆盖:访问 /static/app.js → /data/static/app.js
    }
}

# index 指令:访问目录(URI 以 / 结尾)时按顺序尝试的默认文件
server {
    index index.html index.htm index.php;
    # 访问 / 会依次尝试 index.html → index.htm → index.php
}

# 注意:index 只在 URI 以 / 结尾时生效,对具体文件请求(/foo.html)无效

一个高频混淆点:root 和 alias 的区别。简单说,root 是把 URI 整个拼到路径后面(/images/cat.png/var/www/images/cat.png),而 alias 是替换匹配部分。这个区别我们在 location 章节会详细对比。

6. error_page:自定义错误页

默认的 Nginx 错误页很丑,对用户体验不好。用 error_page 可以把 404、500 等状态码指向自定义页面:

# 自定义错误页(用户体验必备)
server {
    error_page 404 /404.html;          # 404 时返回站点根目录下的 404.html
    error_page 500 502 503 504 /50x.html;   # 所有 5xx 错误统一页面
    error_page 403 https://example.com/forbidden;   # 也可以跳转外部 URL

    location = /404.html {
        root /var/www/errors;          # 错误页放在专用目录
        internal;                      # internal 表示只允许内部跳转访问
    }

    location = /50x.html {
        root /var/www/errors;
        internal;
    }
}

# 配合 return 指令快速返回自定义状态码
location /maintenance {
    return 503;                        # 直接返回 503 状态码
}

internal 指令的作用:禁止外部直接访问这个 location——只能由 error_page 内部跳转过来,防止用户直接访问 /404.html 看到 404 页面却返回 200。

7. 访问控制:限制 IP

有时候你想让后台管理界面只能内网访问,可以用 allow / deny

# 访问控制(限制 IP)
server {
    location /admin {
        allow 192.168.1.0/24;     # 允许内网
        allow 10.0.0.0/8;
        deny all;                 # 其他 IP 拒绝
    }

    location /api {
        # 反向写法:只封禁某些 IP
        deny 123.45.67.89;
        allow all;
    }
}

规则是从上往下匹配,命中即停止。注意:如果用了反向代理,记得配合 set_real_ip_from 拿到真实客户端 IP(不然全是代理的 IP)。

8. 一个完整的虚拟主机配置模板

把上面的指令组合起来,下面是一个生产可用的静态站点模板,可以作为你写新站点的起点:

把这个模板存为 /etc/nginx/conf.d/example.com.confnginx -t && nginx -s reload 即可生效。建议把它作为你的团队标准模板,所有新项目都基于它复制修改。

小结

这一章你学会了用 server 块定义虚拟主机:通过 listen + server_name 区分多个站点,用 root/index/error_page 组织内容,用 allow/deny 做基础访问控制。下一篇我们深入 server 块内部的location 块——这是 Nginx 最强大也最容易绕晕的部分。

← 上一篇 nginx.conf 结构

下一篇 location 匹配规则

✈️💬