服务端阅读 02026年5月28日 07:28
Nginx 如何配置虚拟主机?有哪些配置方式?
Nginx 如何配置虚拟主机?有哪些配置方式?虚拟主机(Virtual Host)是 Nginx 最核心的能力之一——一台服务器、一个 Nginx 进程,就能同时服务几十个甚至上百个网站。面试中这道题考察的不只是"怎么配",更是你对其背后路由机制的理解深度。Nginx 虚拟主机的本质就是 server 块。每个 server 块是一个独立的虚拟主机,Nginx 根据请求的域名、端口或 IP 地址,将请求路由到匹配的 server 块处理。三个关键指令决定了路由规则:listen:监听的地址和端口,如 listen 80 或 listen 443 sslserver_name:匹配请求头中的 Host 字段,支持精确匹配、通配符和正则root:该虚拟主机的网站根目录基于域名的虚拟主机这是生产环境最主流的方式。 多个域名共享同一个 IP,Nginx 根据 HTTP 请求头中的 Host 字段决定将请求交给哪个 server 块处理。这也叫 Name-Based Virtual Host:server { listen 80; server_name example.com www.example.com; root /var/www/example.com; index index.html; access_log /var/log/nginx/example.com.access.log; error_log /var/log/nginx/example.com.error.log; location / { try_files $uri $uri/ =404; }}server { listen 80; server_name test.com www.test.com; root /var/www/test.com; index index.html; access_log /var/log/nginx/test.com.access.log; error_log /var/log/nginx/test.com.error.log; location / { try_files $uri $uri/ =404; }}两个 server 块都监听 80 端口,Nginx 收到请求后先匹配 server_name,命中哪个就走哪个配置。server_name 用空格分隔可以写多个域名,www.example.com 和 example.com 都会命中第一个块。面试追问:如果两个 server 块的 servername 都匹配同一个域名会怎样? Nginx 有明确的匹配优先级:精确匹配 > 最长通配符前缀 > 最长通配符后缀 > 第一个正则匹配 > defaultserver。优先级相同则按配置文件加载顺序,先加载的优先。基于端口的虚拟主机通过不同端口区分服务,适合将管理后台、API 服务与主站做物理隔离:# 主站server { listen 80; server_name example.com; root /var/www/example.com; index index.html; location / { try_files $uri $uri/ =404; }}# 管理后台server { listen 8080; server_name example.com; root /var/www/example.com/admin; index index.html; location / { try_files $uri $uri/ =404; }}# HTTPS 安全服务server { listen 8443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; root /var/www/example.com/secure; index index.html; location / { try_files $uri $uri/ =404; }}端口方式的缺点是用户需要显式指定端口号(如 example.com:8080),80 和 443 之外的端口对用户不友好,因此一般只用于内部服务或管理入口。基于 IP 地址的虚拟主机服务器绑定多个 IP 时,按 IP 地址区分站点。这种方式在早期互联网常用,但在现代云环境下已很少使用——公网 IP 资源有限且费用高,基于域名的方式更经济:server { listen 192.168.1.100:80; server_name example.com; root /var/www/example.com; index index.html; location / { try_files $uri $uri/ =404; }}server { listen 192.168.1.101:80; server_name example.com; root /var/www/example.com/mirror; index index.html; location / { try_files $uri $uri/ =404; }}通配符与正则域名匹配子域名多且动态变化时,逐一配置 server_name 不现实。Nginx 提供通配符和正则两种高级匹配:通配符匹配——用 * 匹配子域名:server { listen 80; server_name *.example.com; location / { set $subdomain $host; if ($subdomain ~* ^(.*)\.example\.com$) { set $subdomain $1; } root /var/www/subdomains/$subdomain; index index.html; }}# 默认虚拟主机:兜底处理未匹配的请求server { listen 80 default_server; server_name _; root /var/www/default; return 444; # 直接关闭连接,比 404 更安全}default_server 是安全防线——任何未匹配到 server_name 的请求都会走这个块。建议返回 444(Nginx 特有状态码,直接关闭连接),比返回 404 更能防止信息泄露。正则表达式匹配——用命名捕获组提取子域名:server { listen 80; server_name ~^(?<subdomain>.+)\.example\.com$; root /var/www/example.com/$subdomain; index index.html; location / { try_files $uri $uri/ =404; }}server { listen 80; server_name ~^(?<user>.+)\.users\.example\.com$; root /var/www/users/$user; index index.html; location / { try_files $uri $uri/ =404; }}?<name> 是命名捕获组,提取的值以 $name 变量在配置中引用。通配符只能做简单的 * 匹配,正则则能处理复杂的域名模式——实际项目中正则用得更多。HTTPS 虚拟主机生产环境必须启用 HTTPS,标准做法是 HTTP 301 永久跳转到 HTTPS:# HTTP -> HTTPS 301 跳转server { listen 80; server_name example.com; return 301 https://$server_name$request_uri;}# HTTPS 服务server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; root /var/www/example.com; index index.html; location / { try_files $uri $uri/ =404; }}http2 参数直接在 listen 指令中启用 HTTP/2,无需额外编译模块。ssl_session_cache 将 SSL 会话缓存 10 分钟,客户端重连时可以复用,避免完整的 TLS 握手,显著提升 HTTPS 性能。SSL 协议只保留 TLSv1.2 和 TLSv1.3,禁用所有不安全的旧版本。反向代理虚拟主机虚拟主机在生产中最常见的用途是反向代理——不同域名转发到不同的后端服务,Nginx 作为网关统一入口:upstream backend1 { server 192.168.1.100:8080; server 192.168.1.101:8080;}upstream backend2 { server 192.168.1.200:8080; server 192.168.1.201:8080;}server { listen 80; server_name api.example.com; location / { proxy_pass http://backend1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}server { listen 80; server_name admin.example.com; location / { proxy_pass http://backend2; 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 三件套必须配置:Host 让后端知道原始域名,X-Real-IP 传递客户端真实 IP,X-Forwarded-For 追加代理链路。不加这些头,后端拿到的全是 Nginx 的内网 IP,日志和鉴权都会出问题。多域名共享配置多个域名配置相似、仅 root 路径不同时,用 map 指令消除重复:map $host $root_path { example.com /var/www/example.com; test.com /var/www/test.com; default /var/www/default;}server { listen 80; server_name example.com test.com; root $root_path; index index.html; location / { try_files $uri $uri/ =404; }}map 在请求处理阶段根据 $host 变量的值映射到对应目录,一个 server 块就能服务多个域名。当域名数量超过 5 个且配置差异仅在路径时,这种方式比逐个写 server 块更易维护。配置文件分离与站点管理生产环境中,千万不要把所有虚拟主机堆在 nginx.conf 一个文件里。用 include 拆分:# /etc/nginx/nginx.confhttp { include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*;}每个虚拟主机一个配置文件,放在 sites-available 目录,通过符号链接启用:# 启用站点ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/# 禁用站点rm /etc/nginx/sites-enabled/example.com# 测试配置语法(修改后必做)nginx -t# 重新加载配置(不影响正在处理的请求)nginx -s reloadnginx -t 是生产操作的铁律——修改配置后先检测语法,通过后再 reload。reload 是平滑重载,Nginx 会等旧请求处理完再切换到新配置,不会中断服务。而 restart 会直接杀掉工作进程,正在处理的请求会断开。PHP 应用虚拟主机WordPress、Laravel 等 PHP 应用需要配置 FastCGI 传递给 PHP-FPM:server { listen 80; server_name example.com; root /var/www/example.com; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; }}try_files 中的 /index.php?$query_string 是 PHP 框架的标配写法——先尝试找静态文件,找不到就转发给 index.php 处理,这是 Laravel 等框架 URL 重写的基础。SCRIPT_FILENAME 必须用 $document_root 拼接,否则 PHP-FPM 找不到脚本文件。静态站点虚拟主机纯静态站点可以做更激进的优化——关日志、开压缩、长缓存:server { listen 80; server_name static.example.com; root /var/www/static; index index.html; gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript; location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } location / { try_files $uri $uri/ =404; }}静态资源关闭 access_log 能显著减少磁盘 IO。高流量站点中,日志写入是主要瓶颈之一,对不需要统计的静态资源关闭日志是常规优化手段。生产环境完整配置模板综合安全、性能和可维护性的完整配置,可作为新项目的起点:server { listen 80; server_name example.com www.example.com; return 301 https://$server_name$request_uri;}server { listen 443 ssl http2; server_name example.com www.example.com; # SSL ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 站点根目录 root /var/www/example.com; index index.php index.html; # 日志 access_log /var/log/nginx/example.com.access.log; error_log /var/log/nginx/example.com.error.log; # 安全头 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript; # 静态资源长缓存 location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # PHP-FPM location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # 主路由 location / { try_files $uri $uri/ /index.php?$query_string; } # 禁止访问隐藏文件(.git、.env 等) location ~ /\. { deny all; access_log off; log_not_found off; }}配置要点总结三种类型的选用原则:基于域名是首选方案,一个 IP 托管所有站点,配置简单且用户无感知;基于端口适合内部服务隔离,但需要用户感知端口;基于 IP 在公网环境下已基本淘汰。server_name 匹配优先级(面试必考):精确匹配 > 通配符前缀(*.example.com)> 通配符后缀(example.*)> 正则表达式 > default_server。优先级相同的,按配置文件加载顺序决定。配置排错四步法:nginx -t 检查语法 -> nginx -s reload 确认重载 -> 检查 server_name 是否匹配请求域名 -> 查看 error.log 中的具体报错。大部分"配置不生效"的问题,要么是忘记 reload,要么是 server_name 写错了。