虚拟主机 #

概述 #

虚拟主机允许在一台服务器上托管多个网站,Caddy 通过简单的配置即可实现多站点、多域名的管理。

text
┌─────────────────────────────────────────────────────────────┐
│                    虚拟主机架构                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                    ┌─────────┐                              │
│                    │  Caddy  │                              │
│                    └────┬────┘                              │
│                         │                                   │
│         ┌───────────────┼───────────────┐                   │
│         │               │               │                   │
│         ▼               ▼               ▼                   │
│   ┌───────────┐  ┌───────────┐  ┌───────────┐             │
│   │site1.com  │  │site2.com  │  │site3.com  │             │
│   │  站点 1    │  │  站点 2    │  │  站点 3    │             │
│   └───────────┘  └───────────┘  └───────────┘             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基本配置 #

多站点配置 #

caddyfile
# 站点 1
site1.example.com {
    root * /var/www/site1
    file_server
}

# 站点 2
site2.example.com {
    root * /var/www/site2
    file_server
}

# 站点 3
site3.example.com {
    reverse_proxy localhost:3000
}

多域名共享配置 #

caddyfile
# 多个域名共享同一配置
example.com, www.example.com {
    root * /var/www/html
    file_server
}

通配符域名 #

caddyfile
# 匹配所有子域名
*.example.com {
    root * /var/www/{labels.1}
    file_server
}

站点地址格式 #

域名格式 #

caddyfile
# 标准域名
example.com { }

# 带端口
example.com:8080 { }

# 通配符
*.example.com { }

# 多域名
example.com, www.example.com, app.example.com { }

IP 地址格式 #

caddyfile
# 监听所有接口
:80 { }
:443 { }

# 监听特定 IP
192.168.1.100 { }
192.168.1.100:8080 { }

# 本地地址
localhost { }
127.0.0.1 { }

混合格式 #

caddyfile
# 域名 + IP 混合
example.com, 192.168.1.100:8080 {
    respond "Mixed address"
}

站点匹配优先级 #

匹配规则 #

Caddy 按照以下优先级匹配站点:

text
1. 精确域名匹配
2. 通配符域名匹配(更具体的优先)
3. 默认站点(:80, :443)

示例 #

caddyfile
# 精确匹配(最高优先级)
api.example.com {
    respond "API Server"
}

# 通配符匹配(次优先级)
*.example.com {
    respond "Wildcard Match"
}

# 默认站点(最低优先级)
:443 {
    respond "Default Site"
}

配置文件拆分 #

主配置文件 #

caddyfile
# /etc/caddy/Caddyfile
{
    email admin@example.com
}

# 导入站点配置
import sites/*.caddyfile
import sites/*/*.caddyfile

站点配置文件 #

caddyfile
# /etc/caddy/sites/main.caddyfile
example.com {
    root * /var/www/main
    file_server
}
caddyfile
# /etc/caddy/sites/api.caddyfile
api.example.com {
    reverse_proxy localhost:3000
}
caddyfile
# /etc/caddy/sites/blog.caddyfile
blog.example.com {
    root * /var/www/blog
    file_server
    encode gzip
}

按环境拆分 #

caddyfile
# /etc/caddy/Caddyfile
{
    email admin@example.com
}

# 开发环境
import sites/development/*.caddyfile

# 生产环境
# import sites/production/*.caddyfile

动态站点配置 #

基于路径的站点 #

caddyfile
example.com {
    # 主站点
    root * /var/www/main
    file_server
    
    # 子路径映射
    handle /blog/* {
        root * /var/www/blog
        file_server
    }
    
    handle /docs/* {
        root * /var/www/docs
        file_server
    }
    
    handle /api/* {
        reverse_proxy localhost:3000
    }
}

基于子域名的站点 #

caddyfile
# 主站点
example.com {
    root * /var/www/main
    file_server
}

# API 子域名
api.example.com {
    reverse_proxy localhost:3000
}

# 博客子域名
blog.example.com {
    root * /var/www/blog
    file_server
}

# 文档子域名
docs.example.com {
    root * /var/www/docs
    file_server
}

动态根目录 #

caddyfile
# 根据域名动态设置根目录
*.example.com {
    root * /var/www/{host}
    file_server
}

# 或使用标签
*.example.com {
    # {labels.0} = 子域名部分
    # {labels.1} = 主域名部分
    root * /var/www/sites/{labels.0}
    file_server
}

重定向配置 #

域名重定向 #

caddyfile
# www 到非 www
www.example.com {
    redir https://example.com{uri} permanent
}

# 非 www 到 www
example.com {
    redir https://www.example.com{uri} permanent
}

HTTP 到 HTTPS #

caddyfile
# 自动 HTTPS 重定向(Caddy 默认行为)
example.com {
    # Caddy 自动处理 HTTP 到 HTTPS 的重定向
}

# 手动配置
http://example.com {
    redir https://example.com{uri} permanent
}

域名迁移 #

caddyfile
# 旧域名重定向到新域名
old-domain.com {
    redir https://new-domain.com{uri} permanent
}

*.old-domain.com {
    redir https://{labels.0}.new-domain.com{uri} permanent
}

路径重定向 #

caddyfile
example.com {
    # 路径重定向
    redir /old-path /new-path permanent
    
    # 正则重定向
    redir /blog/* /posts/{path.1} permanent
    
    # 条件重定向
    @mobile header User-Agent *Mobile*
    redir @mobile https://m.example.com{uri}
}

共享配置 #

使用片段 #

caddyfile
# 定义共享配置片段
(common) {
    encode gzip zstd
    log {
        output file /var/log/caddy/access.log
    }
    header {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
    }
}

# 使用片段
site1.example.com {
    import common
    root * /var/www/site1
    file_server
}

site2.example.com {
    import common
    root * /var/www/site2
    file_server
}

带参数的片段 #

caddyfile
# 定义带参数的片段
(static-site) {
    root * {args[0]}
    file_server
    encode gzip
    log {
        output file /var/log/caddy/{args[1]}.log
    }
}

# 使用
site1.example.com {
    import static-site /var/www/site1 site1
}

site2.example.com {
    import static-site /var/www/site2 site2
}

认证配置 #

站点级认证 #

caddyfile
# 整站认证
private.example.com {
    basicauth {
        admin $2a$14$Zkx...
    }
    root * /var/www/private
    file_server
}

路径级认证 #

caddyfile
example.com {
    root * /var/www/html
    file_server
    
    # 管理路径需要认证
    handle /admin/* {
        basicauth {
            admin $2a$14$Zkx...
        }
        file_server
    }
}

不同用户认证 #

caddyfile
example.com {
    # 用户区域
    handle /user/* {
        basicauth {
            user1 $2a$14$hash1...
            user2 $2a$14$hash2...
        }
        reverse_proxy localhost:3000
    }
    
    # 管理区域
    handle /admin/* {
        basicauth {
            admin $2a$14$adminhash...
        }
        reverse_proxy localhost:3001
    }
}

日志分离 #

站点独立日志 #

caddyfile
site1.example.com {
    root * /var/www/site1
    file_server
    
    log {
        output file /var/log/caddy/site1.log
        format json
    }
}

site2.example.com {
    root * /var/www/site2
    file_server
    
    log {
        output file /var/log/caddy/site2.log
        format json
    }
}

共享日志配置 #

caddyfile
# 全局日志配置
{
    log default {
        output file /var/log/caddy/access.log
        format json
    }
}

# 站点使用默认日志
site1.example.com {
    root * /var/www/site1
    file_server
}

# 站点覆盖日志配置
site2.example.com {
    root * /var/www/site2
    file_server
    
    log {
        output file /var/log/caddy/site2.log
    }
}

完整示例 #

企业多站点配置 #

caddyfile
# 全局配置
{
    email admin@company.com
    acme_ca https://acme-v02.api.letsencrypt.org/directory
}

# 共享配置片段
(security-headers) {
    header {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        X-XSS-Protection "1; mode=block"
        Referrer-Policy "strict-origin-when-cross-origin"
        -Server
    }
}

(logging) {
    log {
        output file /var/log/caddy/{host}.log {
            roll_size 50mb
            roll_keep 10
        }
        format json
    }
}

# 主站
company.com, www.company.com {
    import security-headers
    import logging
    
    root * /var/www/main
    file_server
    encode gzip zstd
    
    # www 重定向
    @www host www.company.com
    redir @www https://company.com{uri} permanent
}

# API 服务
api.company.com {
    import security-headers
    import logging
    
    reverse_proxy {
        to api-server-1:3000
        to api-server-2:3000
        lb_policy round_robin
        health_uri /health
    }
}

# 文档站点
docs.company.com {
    import security-headers
    import logging
    
    root * /var/www/docs
    file_server
    encode gzip
}

# 博客站点
blog.company.com {
    import security-headers
    import logging
    
    root * /var/www/blog
    file_server
    encode gzip
}

# 内部管理系统
admin.company.com {
    import security-headers
    import logging
    
    basicauth {
        admin $2a$14$Zkx...
    }
    
    reverse_proxy localhost:8080
}

# 开发环境
dev.company.com {
    import security-headers
    
    # 使用测试 CA
    tls internal
    
    reverse_proxy localhost:3000
}

SaaS 多租户配置 #

caddyfile
# 主应用
app.example.com {
    reverse_proxy localhost:3000
}

# 租户子域名
*.app.example.com {
    # 提取租户 ID
    @tenant host {
        labels.0
    }
    
    reverse_proxy localhost:3000 {
        header_up X-Tenant-ID {labels.0}
    }
}

# 自定义域名
# 需要动态配置或使用 API

本地开发配置 #

caddyfile
# 本地开发环境
{
    local_certs
}

# 项目 1
project1.localhost {
    reverse_proxy localhost:3001
    tls internal
}

# 项目 2
project2.localhost {
    reverse_proxy localhost:3002
    tls internal
}

# 项目 3
project3.localhost {
    root * /var/www/project3
    file_server
    tls internal
}

# API 网关
api.localhost {
    handle /users/* {
        reverse_proxy localhost:4001
    }
    
    handle /orders/* {
        reverse_proxy localhost:4002
    }
    
    handle /products/* {
        reverse_proxy localhost:4003
    }
    
    tls internal
}

管理 API #

查看站点配置 #

bash
# 查看所有站点
curl localhost:2019/config/apps/http/servers | jq

# 查看特定服务器
curl localhost:2019/config/apps/http/servers/srv0 | jq

动态添加站点 #

bash
# 添加新站点
curl -X POST \
    localhost:2019/config/apps/http/servers/srv0/routes \
    -H "Content-Type: application/json" \
    -d '{
        "match": [{"host": ["new.example.com"]}],
        "handle": [{
            "handler": "subroute",
            "routes": [{
                "handle": [{
                    "handler": "static_response",
                    "body": "New Site"
                }]
            }]
        }],
        "terminal": true
    }'

动态删除站点 #

bash
# 删除站点(根据索引)
curl -X DELETE \
    localhost:2019/config/apps/http/servers/srv0/routes/0

最佳实践 #

1. 配置文件组织 #

text
/etc/caddy/
├── Caddyfile           # 主配置文件
├── sites/              # 站点配置目录
│   ├── main.caddyfile
│   ├── api.caddyfile
│   └── blog.caddyfile
└── snippets/           # 共享片段
    ├── security.caddyfile
    └── logging.caddyfile

2. 使用片段复用配置 #

caddyfile
# 避免重复配置
(common) {
    encode gzip zstd
    header -Server
}

site1.example.com {
    import common
    # ...
}

3. 合理使用重定向 #

caddyfile
# 规范化域名
www.example.com {
    redir https://example.com{uri} permanent
}

# 主站点
example.com {
    # ...
}

4. 分离日志 #

caddyfile
# 每个站点独立日志
log {
    output file /var/log/caddy/{host}.log
}

下一步 #

现在你已经掌握了虚拟主机配置,接下来学习 SSL/HTTPS 配置 了解如何管理 SSL 证书!

最后更新:2026-03-28