跳到主要内容

虚拟主机

虚拟主机允许在一台服务器上托管多个网站。本章将介绍如何配置 Nginx 虚拟主机。

什么是虚拟主机?

虚拟主机(Virtual Host)是在一台物理服务器上运行多个网站的技术。Nginx 通过 server 块实现虚拟主机,根据请求的域名或端口将请求路由到不同的网站配置。

虚拟主机类型

  1. 基于域名:不同域名指向同一 IP
  2. 基于端口:同一域名,不同端口
  3. 基于 IP:不同 IP 地址

基于域名的虚拟主机

基本配置

# 网站 1:example.com
server {
listen 80;
server_name example.com www.example.com;

root /var/www/example;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}
}

# 网站 2:blog.com
server {
listen 80;
server_name blog.com www.blog.com;

root /var/www/blog;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}
}

解释

  • 每个 server 块代表一个虚拟主机
  • server_name 指定域名
  • root 指定网站根目录
  • Nginx 根据请求的 Host 头选择对应的配置

多域名配置

server {
listen 80;

# 多个域名
server_name example.com
www.example.com
example.net
www.example.net;

root /var/www/example;

location / {
try_files $uri $uri/ /index.html;
}
}

通配符域名

# 匹配所有子域名
server {
listen 80;
server_name *.example.com;

root /var/www/subdomains;

# 根据子域名动态设置目录
location / {
# $1 是正则捕获的子域名部分
root /var/www/$1;
}
}

基于端口的虚拟主机

不同端口配置

# 端口 80
server {
listen 80;
server_name example.com;
root /var/www/main;

location / {
try_files $uri $uri/ /index.html;
}
}

# 端口 8080
server {
listen 8080;
server_name example.com;
root /var/www/admin;

location / {
try_files $uri $uri/ /index.html;
}
}

# 端口 8443
server {
listen 8443 ssl;
server_name example.com;

ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;

root /var/www/secure;

location / {
try_files $uri $uri/ /index.html;
}
}

基于 IP 的虚拟主机

不同 IP 配置

# IP 192.168.1.101
server {
listen 192.168.1.101:80;
server_name example.com;
root /var/www/example;
}

# IP 192.168.1.102
server {
listen 192.168.1.102:80;
server_name blog.com;
root /var/www/blog;
}

默认服务器配置

设置默认服务器

当请求不匹配任何 server_name 时,使用默认服务器:

# 默认服务器(捕获所有未匹配的请求)
server {
listen 80 default_server;
server_name _;

# 返回 404 或重定向
return 444; # 关闭连接
# 或
return 301 https://example.com$request_uri;
}

# 正常服务器
server {
listen 80;
server_name example.com;
root /var/www/example;
}

解释

  • default_server 指定默认服务器
  • server_name _ 是一个无效域名,只匹配默认服务器
  • 返回 444 会直接关闭连接,不返回任何响应

目录结构组织

推荐的目录结构

/var/www/
├── example.com/
│ ├── public/
│ │ ├── index.html
│ │ ├── css/
│ │ └── js/
│ └── logs/
│ ├── access.log
│ └── error.log
├── blog.com/
│ ├── public/
│ │ ├── index.html
│ │ └── ...
│ └── logs/
└── api.example.com/
├── public/
└── logs/

配置文件组织

/etc/nginx/
├── nginx.conf # 主配置
├── conf.d/
│ ├── example.com.conf # example.com 配置
│ ├── blog.com.conf # blog.com 配置
│ └── api.example.com.conf # API 配置
└── ssl/
├── example.com.pem
└── example.com.key

主配置文件

# /etc/nginx/nginx.conf
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"';

access_log /var/log/nginx/access.log main;

sendfile on;
keepalive_timeout 65;
gzip on;

# 包含所有虚拟主机配置
include /etc/nginx/conf.d/*.conf;
}

站点配置示例

# /etc/nginx/conf.d/example.com.conf
server {
listen 80;
server_name example.com www.example.com;

# 重定向到 HTTPS
return 301 https://example.com$request_uri;
}

server {
listen 443 ssl http2;
server_name example.com www.example.com;

# SSL 配置
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;

# 网站根目录
root /var/www/example.com/public;
index index.html index.php;

# 日志
access_log /var/www/example.com/logs/access.log;
error_log /var/www/example.com/logs/error.log;

# 网站配置
location / {
try_files $uri $uri/ /index.html;
}

# 静态资源
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}

# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
}

实际场景配置

场景 1:个人博客

server {
listen 80;
server_name blog.example.com;

root /var/www/blog;
index index.html;

# 静态博客(如 Hexo、Hugo)
location / {
try_files $uri $uri/ =404;
}

# 文章页
location /posts/ {
try_files $uri $uri/ /posts/index.html;
}

# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}

场景 2:前后端分离

# 前端(Vue/React)
server {
listen 80;
server_name example.com;

root /var/www/frontend/dist;
index index.html;

# SPA 路由
location / {
try_files $uri $uri/ /index.html;
}

# API 代理
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

# 静态资源
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}

场景 3:WordPress 站点

server {
listen 80;
server_name wordpress.example.com;

root /var/www/wordpress;
index index.php index.html;

# WordPress 固定链接
location / {
try_files $uri $uri/ /index.php?$args;
}

# PHP 处理
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

# 增加缓冲区
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
}

# 静态资源
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}

# 禁止访问敏感文件
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}

# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
}

场景 4:子目录部署多个应用

server {
listen 80;
server_name example.com;

# 主站点
root /var/www/main;

location / {
try_files $uri $uri/ /index.html;
}

# 博客子目录
location /blog/ {
alias /var/www/blog/;
try_files $uri $uri/ /blog/index.html;
}

# 管理后台
location /admin/ {
alias /var/www/admin/;
try_files $uri $uri/ /admin/index.html;
}

# API 服务
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}

最佳实践

1. 使用配置片段

创建可复用的配置片段:

# /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

引用配置片段:

server {
listen 443 ssl http2;
server_name example.com;

ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;

# 引入 SSL 配置
include /etc/nginx/snippets/ssl-params.conf;
}

2. 日志分离

server {
# 每个站点独立日志
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
}

3. 错误页面

server {
# 自定义错误页面
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

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

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

4. 安全头

server {
# 安全响应头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
}

5. 隐藏版本信息

# 在 http 块中
server_tokens off;

# 或在主配置文件中的 http 块

常见问题

1. 域名解析问题

# 测试域名解析
nslookup example.com
dig example.com

# 检查本地 hosts 文件
cat /etc/hosts

2. 配置冲突

# 检查配置语法
nginx -t

# 检查站点配置
nginx -T | grep server_name

3. 目录权限

# 设置正确权限
sudo chown -R nginx:nginx /var/www/example.com
sudo chmod -R 755 /var/www/example.com

小结

本章我们学习了:

  1. 虚拟主机的概念和类型
  2. 基于域名的虚拟主机配置
  3. 基于端口的虚拟主机配置
  4. 默认服务器设置
  5. 配置文件组织方式
  6. 实际场景配置示例
  7. 最佳实践和安全建议

练习

  1. 配置两个基于域名的虚拟主机
  2. 配置一个前后端分离的站点
  3. 添加自定义错误页面
  4. 配置安全响应头

延伸阅读