VPSKnow

Web 服务器搭建完全指南:Nginx / Apache / Caddy

中级
50分钟

Web 服务器是网站的引擎。本指南帮您在 Nginx、Apache、Caddy 中做出正确选择,并手把手完成安装、PHP-FPM 集成、反向代理,以及用 Certbot 或 acme.sh 申请 SSL 证书——从零到一个完整的 HTTPS 站点。

⚖️ 选型对比:Nginx / Apache / Caddy

在开始安装前,先了解三位"选手"的核心差异,选对工具能让您事半功倍。

🟢

Nginx

高性能反向代理首选
✅ 优点
  • +异步事件驱动,万级并发轻松应对
  • +内存占用极低(约 2-3MB/worker)
  • +反向代理与负载均衡功能强大
  • +静态文件服务性能业界最优
⚠️ 注意
  • 配置语法需要一定学习成本
  • 动态配置需要 reload(无 .htaccess)
🎯 最适合

静态网站、反向代理、API 网关、高并发场景

🔴

Apache

老牌生态最完整
✅ 优点
  • +.htaccess 支持目录级动态配置
  • +PHP mod_php 深度集成,零配置
  • +模块生态最丰富,文档最完善
  • +兼容大量老旧 PHP 应用
⚠️ 注意
  • 高并发下每连接一线程,内存消耗大
  • 默认配置性能不如 Nginx
🎯 最适合

传统 PHP 应用(WordPress/Drupal)、需要 .htaccess 的场景

🔵

Caddy

零配置自动 HTTPS
✅ 优点
  • +自动申请、配置、续期 SSL 证书
  • +配置文件极简,3行搞定 HTTPS 站点
  • +原生支持 HTTP/2 和 HTTP/3
  • +Go 语言编写,单二进制无依赖
⚠️ 注意
  • 生态和社区不如 Nginx/Apache 成熟
  • 高级调优选项较少
🎯 最适合

个人项目、快速原型、不想折腾 SSL 的场景

💡 一句话推荐:不知道选哪个?选 Nginx——它覆盖了 95% 的场景。只有两种例外:① 您的 PHP 应用依赖 .htaccess 动态配置 → 选 Apache;② 您不想碰 SSL 配置,要最省心 → 选 Caddy。

🟢 Nginx:安装、配置与性能调优

Nginx(发音 "engine-x")以异步事件驱动架构著称,单进程可处理数万并发连接,是目前最主流的 Web 服务器,全球市占率超过 35%。

安装与验证

安装 Nginx
# ── Ubuntu / Debian ──────────────────────────────────────────────────────────
sudo apt update && sudo apt install nginx -y

# 启动并设置开机自启
sudo systemctl enable --now nginx

# 验证安装:访问 http://服务器IP 应看到 "Welcome to nginx!" 页面
curl -I http://localhost   # 应返回 200 OK

# ── CentOS / AlmaLinux / RHEL ─────────────────────────────────────────────────
sudo yum install nginx -y
sudo systemctl enable --now nginx

📁 Nginx 核心目录速查

/etc/nginx/nginx.conf 主配置文件:全局 worker、日志、http 块
/etc/nginx/conf.d/*.conf 站点配置目录(CentOS 直接生效)
/etc/nginx/sites-available/ 站点配置存放目录(Ubuntu/Debian)
/etc/nginx/sites-enabled/ 已激活的站点(软链接到 sites-available)
/var/log/nginx/access.log 访问日志(每条请求)
/var/log/nginx/error.log 错误日志(排查问题首选)

静态网站配置

/etc/nginx/conf.d/mysite.conf
# 文件路径:/etc/nginx/conf.d/mysite.conf
# Ubuntu/Debian 也可放到 /etc/nginx/sites-available/mysite.conf(需创建软链接激活)

server {
    listen 80;
    listen [::]:80;                          # 同时监听 IPv6
    server_name example.com www.example.com; # 您的域名

    root /var/www/mysite;                    # 网站根目录
    index index.html index.htm index.php;    # 默认首页文件顺序

    # 日志(每个站点独立日志,便于排查)
    access_log /var/log/nginx/mysite_access.log;
    error_log  /var/log/nginx/mysite_error.log warn;

    # 静态文件请求处理:先找文件,再找目录,最后返回 404
    location / {
        try_files $uri $uri/ =404;
    }

    # 静态资源长期缓存(图片/CSS/JS/字体)
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2|svg)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # 禁止访问隐藏文件(.git/.env 等)
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
}

性能调优:nginx.conf 全局优化

/etc/nginx/nginx.conf(性能调优版)
# 文件路径:/etc/nginx/nginx.conf
# 以下配置针对 2核4GB 服务器调优,根据实际硬件调整数值

# ── 全局块 ────────────────────────────────────────────────────────────────────
worker_processes auto;          # 自动匹配 CPU 核心数(推荐)
worker_rlimit_nofile 65535;     # 每个 worker 进程最大打开文件数

events {
    worker_connections 4096;    # 每个 worker 的最大并发连接数
    multi_accept on;            # 一次接受所有新连接(提升突发连接效率)
    use epoll;                  # Linux 最高效的 I/O 事件模型
}

http {
    # ── 基础安全 ──────────────────────────────────────────────────────────────
    server_tokens off;          # 隐藏 Nginx 版本号(防止版本探测)
    
    # ── 连接优化 ──────────────────────────────────────────────────────────────
    keepalive_timeout 65;       # 保持 HTTP 长连接 65 秒
    keepalive_requests 1000;    # 单个长连接最多处理 1000 个请求
    client_header_timeout 15;   # 读取请求头超时(防慢速攻击)
    client_body_timeout   15;
    send_timeout          15;

    # ── Gzip 压缩(减少 60-80% 传输体积)────────────────────────────────────
    gzip on;
    gzip_vary on;               # 告知代理服务器缓存 gzip 版本
    gzip_proxied any;           # 对代理请求也压缩
    gzip_comp_level 6;          # 压缩级别 1-9,6 是性能与压缩率的最佳平衡点
    gzip_min_length 1000;       # 小于 1KB 的响应不压缩
    gzip_types
        text/plain text/css text/xml text/javascript
        application/json application/javascript application/xml
        application/x-font-ttf font/opentype image/svg+xml;

    # ── 文件缓存(减少磁盘 I/O)───────────────────────────────────────────────
    open_file_cache max=10000 inactive=30s;  # 缓存最多 10000 个文件描述符
    open_file_cache_valid    60s;
    open_file_cache_min_uses 2;              # 至少访问 2 次才缓存
    open_file_cache_errors   on;

    # ── 请求限速(防 CC 攻击,从第12篇 security-advanced 引入)────────────────
    limit_req_zone $binary_remote_addr zone=perip:10m rate=30r/s;
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    include /etc/nginx/conf.d/*.conf;
}

反向代理配置

/etc/nginx/conf.d/api.conf(反向代理)
# 反向代理:将域名请求转发到后端应用(Node.js/Python/Go 等)
# 文件路径:/etc/nginx/conf.d/api.conf

upstream backend_app {
    # keepalive 保持与后端的持久连接,减少 TCP 握手开销
    keepalive 32;
    server 127.0.0.1:3000;     # 后端应用地址和端口
    # server 127.0.0.1:3001;   # 可以添加多个后端实现负载均衡
}

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

    # 应用连接限速规则
    limit_req zone=perip burst=50 nodelay;

    location / {
        proxy_pass http://backend_app;
        proxy_http_version 1.1;

        # WebSocket 支持(如果后端有 WS 连接需要这两行)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # 传递真实客户端信息到后端
        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;

        # 超时配置(后端响应超过此时间返回 504)
        proxy_connect_timeout  10s;
        proxy_send_timeout     60s;
        proxy_read_timeout     60s;

        # 缓冲区(防止后端慢速导致 worker 阻塞)
        proxy_buffering on;
        proxy_buffer_size 8k;
        proxy_buffers 16 8k;
    }
}

负载均衡配置

upstream 负载均衡配置
# 负载均衡:将流量分散到多台后端服务器
upstream web_cluster {
    # least_conn:将请求发到当前连接数最少的服务器(适合耗时不均的请求)
    least_conn;

    server backend1.example.com:8080 weight=3;  # weight 越大获得越多流量
    server backend2.example.com:8080 weight=2;
    server backend3.example.com:8080 weight=1;

    # backup:仅在其他服务器全部不可用时启用
    # server backup.example.com:8080 backup;

    keepalive 64;
}

# 其他负载均衡策略:
# round_robin(默认):轮询,最简单
# ip_hash:同一 IP 始终路由到同一后端(适合 Session 不共享的场景)
# hash $request_uri consistent:按 URL 哈希(适合缓存命中率优化)

🐘 实战:Nginx + PHP-FPM 完整部署

PHP-FPM(FastCGI Process Manager)是 Nginx 处理 PHP 的标准方案。与 Apache 的 mod_php 不同,PHP-FPM 作为独立进程运行,Nginx 通过 Unix Socket 与其通信,资源占用更低,性能更好。

安装 PHP 8.3 + PHP-FPM

安装 PHP 8.3 + 常用扩展
# ── 安装 PHP 8.3 和 PHP-FPM ────────────────────────────────────────────────
# Ubuntu 22.04+ / Debian 12 使用 ondrej/php PPA 获取最新版
apt install -y software-properties-common
add-apt-repository ppa:ondrej/php -y   # Ubuntu 专用,Debian 跳过此步
apt update

# 安装 PHP-FPM 和 WordPress 常用扩展
apt install -y php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd                php8.3-intl php8.3-mbstring php8.3-xml php8.3-zip                php8.3-bcmath php8.3-imagick

# 启动 PHP-FPM
systemctl enable --now php8.3-fpm

# 验证 PHP-FPM 正在运行(监听 Unix socket)
ls -la /run/php/php8.3-fpm.sock   # 应存在此 socket 文件

Nginx 配置 PHP-FPM(WordPress 完整示例)

/etc/nginx/conf.d/wordpress.conf
# 文件路径:/etc/nginx/conf.d/wordpress.conf
# WordPress + Nginx + PHP-FPM 完整配置

server {
    listen 80;
    server_name yourblog.com www.yourblog.com;
    root /var/www/wordpress;
    index index.php index.html;

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

    # PHP 文件通过 FastCGI 交给 PHP-FPM 处理
    location ~ \.php$ {
        # 防止 Nginx 处理不存在的 PHP 文件(安全)
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;

        # 连接 PHP-FPM(Unix socket 比 TCP 快约 10%)
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;

        # 传递标准 FastCGI 参数
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

        # FastCGI 缓冲(减少 PHP-FPM 进程等待时间)
        fastcgi_buffering on;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 16 16k;
        fastcgi_read_timeout 120s;
    }

    # WordPress 上传文件大小限制
    client_max_body_size 64M;

    # 禁止访问 WordPress 敏感文件
    location ~* /wp-config\.php { deny all; }
    location ~* /xmlrpc\.php    { deny all; }  # 禁用 XML-RPC 防暴力破解
    location ~* /\.               { deny all; }  # 禁止访问隐藏文件
}

PHP-FPM 进程池调优

/etc/php/8.3/fpm/pool.d/www.conf
# 文件路径:/etc/php/8.3/fpm/pool.d/www.conf
# PHP-FPM 进程池配置(根据服务器内存调整)

[www]
user  = www-data
group = www-data

# ── 进程管理模式 ──────────────────────────────────────────────────────────────
# dynamic(推荐):根据负载动态调整进程数,平衡性能与内存
pm = dynamic

# 进程数参考公式(以每个 PHP 进程占 50MB 内存为例):
# pm.max_children = 可用内存(MB) / 50
pm.max_children   = 20   # 1GB 内存约设 20
pm.start_servers  = 5    # 启动时初始化 5 个进程
pm.min_spare_servers = 3 # 最少保留 3 个空闲进程
pm.max_spare_servers = 8 # 最多保留 8 个空闲进程

# 每个进程处理 500 个请求后重启(防内存泄漏)
pm.max_requests = 500

# ── PHP 运行时配置(覆盖 php.ini)────────────────────────────────────────────
php_value[memory_limit]       = 256M
php_value[upload_max_filesize] = 64M
php_value[post_max_size]       = 64M
php_value[max_execution_time]  = 120

# 开启 PHP-FPM 慢日志(记录超过 5 秒的请求,性能调优利器)
slowlog = /var/log/php8.3-fpm-slow.log
request_slowlog_timeout = 5s

🔴 Apache:安装、虚拟主机与 PHP 集成

Apache 拥有 30 年历史,其 mod_php 深度集成让 PHP 应用的部署极其简单,.htaccess 的目录级动态配置也让它在共享主机环境中无可替代。

安装 Apache

安装 Apache
# ── Ubuntu / Debian ──────────────────────────────────────────────────────────
sudo apt update && sudo apt install apache2 -y
sudo systemctl enable --now apache2

# ── CentOS / RHEL(软件包名为 httpd)─────────────────────────────────────────
sudo yum install httpd -y
sudo systemctl enable --now httpd

# 验证:访问 http://服务器IP 应看到 Apache 欢迎页
curl -I http://localhost

虚拟主机配置

/etc/apache2/sites-available/mysite.conf
# 文件路径:/etc/apache2/sites-available/mysite.conf(Ubuntu)
# CentOS:/etc/httpd/conf.d/mysite.conf

<VirtualHost *:80>
    ServerName   example.com
    ServerAlias  www.example.com
    DocumentRoot /var/www/mysite

    <Directory /var/www/mysite>
        Options -Indexes +FollowSymLinks   # 禁止目录浏览,允许符号链接
        AllowOverride All                  # 允许 .htaccess(如不需要改 None 提升性能)
        Require all granted
    </Directory>

    # 日志(每站点独立)
    ErrorLog  ${APACHE_LOG_DIR}/mysite_error.log
    CustomLog ${APACHE_LOG_DIR}/mysite_access.log combined
</VirtualHost>

# ── Ubuntu 激活站点 ───────────────────────────────────────────────────────────
# sudo a2ensite mysite.conf
# sudo a2enmod rewrite                    # 启用 URL 重写模块(WordPress 必需)
# sudo systemctl reload apache2

.htaccess 配置(URL 重写与安全规则)

/var/www/mysite/.htaccess
# 文件路径:/var/www/mysite/.htaccess
# 注意:AllowOverride All 时 Apache 才会读取此文件

# ── 开启 URL 重写 ─────────────────────────────────────────────────────────────
RewriteEngine On

# 强制 HTTPS(HTTP 全部跳转到 HTTPS)
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

# 去除 www 前缀(统一规范 URL)
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

# WordPress 固定链接(所有 PHP 不存在的路径交给 index.php)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [L,QSA]

# 禁止访问敏感文件
<FilesMatch "^(\.env|\.git|wp-config\.php|readme\.html)">
    Require all denied
</FilesMatch>

常用模块管理与 PHP 集成

sudo a2enmod rewrite

URL 重写(WordPress/Laravel 必需)

sudo a2enmod ssl

SSL/HTTPS 支持

sudo a2enmod headers

自定义 HTTP 响应头

sudo a2enmod deflate

Gzip 压缩

sudo a2enmod expires

浏览器缓存控制

sudo a2ensite mysite

激活站点配置

模块变更后执行 sudo systemctl restart apache2 生效。PHP 集成:sudo apt install php libapache2-mod-php php-mysql -y,安装后 Apache 自动加载 PHP 模块,无需额外配置。

🔵 Caddy:零配置自动 HTTPS

Caddy 是最省心的 Web 服务器——它会自动申请 Let's Encrypt 证书、自动续期、自动将 HTTP 跳转到 HTTPS,您只需写几行配置文件。

安装 Caddy

安装 Caddy(官方 APT 源)
# ── Ubuntu / Debian ──────────────────────────────────────────────────────────
# 添加 Caddy 官方 APT 源
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key'   | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt'   | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddy -y

# ── CentOS / RHEL ─────────────────────────────────────────────────────────────
# 参考官方文档:https://caddyserver.com/docs/install#fedora-redhat-centos

# 验证安装
systemctl status caddy
caddy version

最简 Caddyfile(3行搞定 HTTPS 站点)

/etc/caddy/Caddyfile
# 文件路径:/etc/caddy/Caddyfile
# 这 3 行就是一个完整的 HTTPS 静态网站!

example.com {
    root * /var/www/mysite   # 网站根目录
    file_server              # 开启文件服务器(自动处理静态文件)
    # Caddy 自动为 example.com 申请 Let's Encrypt 证书,自动续期,自动 HTTP→HTTPS 跳转
}

Caddy 的自动化魔法: 只要域名已解析到本服务器,Caddy 就会自动完成:① 向 Let's Encrypt 申请证书 → ② 配置 HTTPS → ③ 设置 HTTP 自动跳转 → ④ 90 天后自动续期。前提:80 和 443 端口对外开放,且服务器能访问公网。

高级 Caddyfile(多域名、反代、PHP-FPM)

/etc/caddy/Caddyfile(高级版)
# 文件路径:/etc/caddy/Caddyfile
# 完整的高级配置示例

# 主站:静态 + PHP 博客
example.com {
    root * /var/www/mysite
    file_server
    encode gzip            # 启用 Gzip 压缩

    # 反向代理特定路径到后端 API
    reverse_proxy /api/* localhost:3000

    # PHP-FPM 集成
    php_fastcgi unix//run/php/php8.3-fpm.sock

    # 安全响应头
    header {
        X-Content-Type-Options  "nosniff"
        X-Frame-Options         "DENY"
        Referrer-Policy         "strict-origin-when-cross-origin"
        -Server                 # 删除 Server 响应头
    }

    # 自定义日志格式
    log {
        output file /var/log/caddy/example.log {
            roll_size 100mb
            roll_keep 10
        }
        format console
    }
}

# API 子域名:纯反向代理
api.example.com {
    reverse_proxy localhost:8080
}

# 图床子域名:静态文件服务
cdn.example.com {
    root * /var/www/uploads
    file_server browse   # browse 显示目录列表(内部使用)
}

# 重载配置(不中断现有连接)
# sudo systemctl reload caddy

🔒 SSL 证书:Certbot 与 acme.sh 双方案

HTTPS 已是现代网站的标配,对 SEO 和用户信任都有直接影响。以下提供两套证书申请方案:Certbot(官方工具,自动修改 Nginx/Apache 配置)和 acme.sh(纯 Shell 脚本,支持泛域名和 DNS 验证,无需开放 80 端口)。

🤖Certbot

  • 官方 EFF 维护,文档最全
  • 自动修改 Nginx/Apache 配置
  • 新手最友好

适合:首次配置 SSL,单一域名场景

acme.sh

  • 支持 *.example.com 泛域名
  • DNS 验证无需开放 80 端口
  • 支持多个 CA(Let's Encrypt/ZeroSSL)

适合:泛域名证书、CDN 场景、无 80 端口

方案一:Certbot(推荐新手)

Certbot 安装与申请证书
# ── Ubuntu / Debian ──────────────────────────────────────────────────────────
sudo apt install certbot python3-certbot-nginx python3-certbot-apache -y

# ── CentOS / RHEL(需要 EPEL)─────────────────────────────────────────────────
sudo dnf install epel-release -y
sudo dnf install certbot python3-certbot-nginx python3-certbot-apache -y
# ── 方式一:自动模式(推荐,Certbot 自动修改 Nginx/Apache 配置)─────────────
# Nginx:
sudo certbot --nginx -d example.com -d www.example.com
# 按提示:输入邮箱 → 同意条款 → 选择是否跳转 HTTPS → 完成

# Apache:
sudo certbot --apache -d example.com -d www.example.com

# ── 方式二:certonly 模式(只获取证书,手动配置)──────────────────────────────
# 适合:使用 Docker 或非标准路径的场景
sudo certbot certonly --webroot   -w /var/www/mysite   -d example.com   -d www.example.com   --email admin@example.com   --agree-tos

# 证书文件位置:
# 证书:/etc/letsencrypt/live/example.com/fullchain.pem
# 私钥:/etc/letsencrypt/live/example.com/privkey.pem

# ── 验证自动续期 ──────────────────────────────────────────────────────────────
sudo certbot renew --dry-run     # 模拟续期(不实际执行)
sudo certbot certificates        # 查看所有证书及到期时间

方案二:acme.sh 申请泛域名证书(进阶推荐)

acme.sh + Cloudflare DNS API 申请 *.example.com
# ── acme.sh 申请泛域名证书(优于 Certbot 的场景:泛域名、DNS 验证、多 CA)──
# 优势:纯 Shell 脚本无依赖、支持 *.example.com 泛域名、支持 ZeroSSL/Let's Encrypt

# 1. 安装 acme.sh
curl https://get.acme.sh | sh -s email=admin@example.com
source ~/.bashrc   # 或重新打开终端

# 2. 配置 Cloudflare DNS API(用于 DNS-01 验证,无需 80 端口开放)
# 在 CF 后台 → 我的个人资料 → API 令牌 → 创建令牌(Zone DNS 编辑权限)
export CF_Token="your_cloudflare_api_token"
export CF_Zone_ID="your_zone_id"

# 3. 申请泛域名证书(* 号匹配所有子域名)
acme.sh --issue   --dns dns_cf   -d example.com   -d "*.example.com"   --server letsencrypt    # 可选 --server zerossl 使用 ZeroSSL

# 4. 安装证书到 Nginx 目录
acme.sh --install-cert   -d example.com   -d "*.example.com"   --cert-file      /etc/nginx/ssl/example.com/cert.pem   --key-file       /etc/nginx/ssl/example.com/key.pem   --fullchain-file /etc/nginx/ssl/example.com/fullchain.pem   --reloadcmd      "systemctl reload nginx"   # 安装后自动 reload

# 5. acme.sh 自动添加 Cron 任务,每天检查并在到期前 30 天自动续期
# 查看:crontab -l | grep acme

Nginx HTTPS 完整配置(含 HTTP/2 和安全响应头)

/etc/nginx/conf.d/mysite.conf(HTTPS 完整版)
# 申请证书后,将 HTTP server 块升级为 HTTPS
# 文件路径:/etc/nginx/conf.d/mysite.conf

# HTTP → HTTPS 跳转
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# HTTPS 主配置
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;                                    # 启用 HTTP/2(提升多资源加载速度)
    server_name example.com www.example.com;

    # 证书路径(Certbot 自动填充 / acme.sh 手动指定)
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # ── SSL 安全配置(Mozilla 现代级别)──────────────────────────────────────
    ssl_protocols TLSv1.2 TLSv1.3;              # 只允许 TLS 1.2/1.3,禁用旧版
    ssl_prefer_server_ciphers off;               # 让客户端选择最佳密码套件
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;

    # SSL Session 复用(减少重复握手开销)
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;

    # HSTS:强制浏览器永远使用 HTTPS(慎用,设置后很难撤销)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # 安全响应头
    add_header X-Frame-Options         "SAMEORIGIN"  always;
    add_header X-Content-Type-Options  "nosniff"     always;
    add_header X-XSS-Protection        "1; mode=block" always;
    server_tokens off;

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

    location / {
        try_files $uri $uri/ =404;
    }
}
💡 SSL 配置质量检测: 配置完成后,访问 SSL Labs 在线测试,输入您的域名,目标是获得 A+ 评级。本文的配置模板已经达到 Mozilla 现代级别安全标准,正确配置后应直接获得 A+。

🔧 故障排查速查手册

故障排查通用命令
# ════════════════════════════════════════════════════════════════════
#  Web 服务器故障排查速查手册
# ════════════════════════════════════════════════════════════════════

# ── 通用:查看服务状态与日志 ──────────────────────────────────────────────────
systemctl status nginx                    # 服务是否运行
journalctl -u nginx -n 50 -p err          # 最近 50 条 error 日志
tail -f /var/log/nginx/error.log          # 实时错误日志

# ── Nginx:配置语法检查与重载 ─────────────────────────────────────────────────
nginx -t                                  # 测试配置文件语法(出错显示具体位置)
systemctl reload nginx                    # 语法无误后优雅重载(不中断连接)
# !永远不要跳过 nginx -t 直接 reload,语法错误会导致 Nginx 完全停止

# ── 端口占用检查 ──────────────────────────────────────────────────────────────
ss -tlnp | grep ':80|:443'               # 查看 80/443 端口被哪个进程占用
lsof -i :80                               # 同上,另一种方式

# ── 权限问题(403 Forbidden)─────────────────────────────────────────────────
# Nginx 以 www-data 用户运行,网站目录必须对其可读
chown -R www-data:www-data /var/www/mysite
chmod -R 755 /var/www/mysite             # 目录 755,文件 644
chmod 644 /var/www/mysite/index.html

# ── PHP-FPM 连接问题 ──────────────────────────────────────────────────────────
systemctl status php8.3-fpm              # FPM 是否运行
ls -la /run/php/php8.3-fpm.sock          # socket 文件是否存在
# 若 socket 不存在,检查 /etc/php/8.3/fpm/pool.d/www.conf 中的 listen 配置

# ── SSL 证书问题 ──────────────────────────────────────────────────────────────
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# 输出 notAfter 即到期时间
certbot certificates                     # 查看所有证书状态
启动 Nginx 报错"端口已被占用"(Address already in use)

80/443 端口被其他进程占用。排查步骤:① ss -tlnp | grep ':80' 查看占用进程;② 常见"凶手":Apache(systemctl stop apache2)、面板安装的 Nginx(systemctl stop openresty)、Python 测试服务器;③ 若需要两个服务共存,给其中一个改端口,前面用 Nginx 做反向代理统一出口。

访问网站显示 403 Forbidden

三种常见原因:① 权限问题chown -R www-data:www-data /var/www/mysite && chmod -R 755 /var/www/mysite;② 缺少索引文件:目录下没有 index.html/index.php,且配置了 try_files ... =404;③ SELinux 拦截(CentOS):chcon -Rt httpd_sys_content_t /var/www/mysite 或临时 setenforce 0 测试是否是 SELinux 原因。

502 Bad Gateway(反向代理场景)

Nginx 无法连接后端服务。检查顺序:① curl http://localhost:3000 直接测试后端是否响应;② systemctl status 后端服务名 确认服务在运行;③ 检查 proxy_pass 中的端口与后端实际监听端口是否一致;④ 查看 tail -f /var/log/nginx/error.log 获取具体错误原因——"connection refused" 表示后端没运行,"timeout" 表示后端运行但响应太慢。

Certbot 申请证书失败

按可能性从高到低排查:① DNS 未生效dig example.com 确认解析到当前服务器 IP;② 80 端口被防火墙拦截ufw allow 80/tcp,Let's Encrypt 的 HTTP-01 验证必须能访问 80 端口;③ Nginx/Apache 未运行:Certbot 需要 Web 服务器配合完成验证;④ 频率限制:同一域名每周最多申请 5 次,超限需等 7 天,可用 --staging 参数测试(不消耗配额);⑤ 备选:改用 acme.sh + DNS API 验证,完全绕开 80 端口问题。

PHP 文件直接下载而不执行(显示代码而不是网页)

Nginx 没有配置 PHP-FPM 处理 .php 文件。确认 Nginx 配置中有正确的 location ~ \.php$ 块,并且 fastcgi_pass 指向的 socket 路径与 PHP-FPM 实际 socket 路径一致(ls /run/php/ 查看实际路径)。Apache 则确认已安装 libapache2-mod-php 并执行 a2enmod php8.3

修改配置后不生效

修改配置后必须重载服务:Nginx → nginx -t && systemctl reload nginx(先测试语法);Apache → apachectl configtest && systemctl reload apache2;Caddy → systemctl reload caddy浏览器缓存也是常见"元凶"——用 Chrome 按 Ctrl+Shift+R 强制刷新,或开无痕窗口测试。Ubuntu 的 sites-available 配置还需要 a2ensite/ln -s 创建软链接激活,否则修改不会被读取。

如何查看 Web 服务器实时日志?

Nginx 错误日志:tail -f /var/log/nginx/error.log;访问日志:tail -f /var/log/nginx/access.log。Apache:tail -f /var/log/apache2/error.log。Caddy:journalctl -u caddy -f。用 grep " 500 \| 502 \| 503 " /var/log/nginx/access.log | tail -20 快速过滤错误请求。详细的日志分析方法参见第 12 篇日志分析指南。

Nginx 性能测试显示并发很低,如何优化?

按影响程度从大到小排查:① worker_processes:确认设为 auto(匹配 CPU 核心数);② worker_connections:从默认 1024 提升到 4096;③ 系统文件描述符限制ulimit -n 默认可能只有 1024,执行 ulimit -n 65535 提升,同时在 nginx.confworker_rlimit_nofile 65535;④ keepalive_timeout:适当延长(65-120s)减少 TCP 握手次数;⑤ 瓶颈在后端:若是反向代理场景,通常瓶颈在 PHP-FPM/数据库而非 Nginx 本身,用 ab -n 1000 -c 100 http://localhost/ 先测静态文件响应速度定位瓶颈。

常见问题解答

Nginx 和 Apache 可以同时安装在一台服务器上吗?

可以安装,但不能同时监听 80/443 端口。经典方案:让 Nginx 监听 80/443 对外,Apache 监听 8080/8888 对内,Nginx 将动态 PHP 请求反代到 Apache(proxy_pass http://127.0.0.1:8080)。但这种架构增加了复杂度,现代方案推荐 Nginx + PHP-FPM 的组合,比 Nginx + Apache 性能更好,资源占用更低。只有当您有无法去掉 .htaccess 的老 PHP 应用时,才值得考虑双服务器方案。

Let's Encrypt 的免费证书和付费证书有什么区别?值得买付费证书吗?

对于绝大多数个人站长和中小企业,Let's Encrypt 免费证书完全够用——它提供的加密强度与付费证书完全相同,浏览器显示的"🔒"小锁也没有区别。付费证书主要差异:① OV(组织验证)证书:CA 会验证公司真实性,证书信息中显示公司名称,适合有公信力要求的企业;② EV(扩展验证)证书:最高级别验证,以前浏览器会显示绿色地址栏(现代浏览器已取消),金融/政府机构使用;③ 通配符付费证书:一次购买可用于所有子域名,而 Let's Encrypt 的泛域名证书通过 acme.sh 可以免费获得。结论:个人博客/企业官网/API 服务用 Let's Encrypt 完全足够;只有银行/证券/政府类网站才需要考虑 OV/EV 付费证书。

服务器已经用了 1Panel 面板管理 Nginx,还需要学手动配置吗?

强烈建议学。原因:① 面板出问题时需要手动排查:当面板显示 "Nginx 配置错误" 时,您需要能读懂 /etc/nginx/conf.d/ 下的具体配置文件来定位问题;② 面板的"自定义配置"框需要你写 Nginx 语法:实现复杂的 location 规则、特殊 Header 配置时,面板只提供了输入框,内容要您自己写;③ 理解底层让您更有掌控感:知道面板的"反向代理"按钮背后生成了什么配置,出了问题才能从容应对。第 13 篇的面板是加速器,本篇是理解地基——两者互为补充,不是替代关系。

HTTP/2 和 HTTP/3 有什么区别?应该开哪个?

HTTP/1.1(默认):每个资源串行请求,浏览器开多个 TCP 连接来并行加载。HTTP/2:单个 TCP 连接上多路复用,一次请求可同时传输多个资源,对多资源页面(CSS/JS/图片多的网站)提升明显(20-30%)。HTTP/3:基于 UDP 的 QUIC 协议,解决了 TCP 的队头阻塞问题,对高延迟/丢包网络(移动网络)有更大提升。推荐配置:在 Nginx 的 listen 443 行加 http2 on; 开启 HTTP/2,这是目前生产环境的最佳实践,几乎零风险。HTTP/3 需要 Nginx 1.25.0+ 且系统支持 QUIC,目前仍属于进阶配置,个人站点可以尝试,企业生产环境谨慎开启。

WordPress 部署在 Nginx 上,固定链接 404,是什么原因?

WordPress 的固定链接(如 /2026/03/my-post/)依赖 URL 重写,Nginx 需要正确配置 try_files。在 Nginx 的 server 块 location / 中添加:try_files $uri $uri/ /index.php?$args;——这行的含义是:先找文件,再找目录,都不存在就交给 index.php 处理(WordPress 的路由入口)。缺少这行就会导致固定链接 404。如果是 Apache,则需要确认已启用 mod_rewrite 并且 AllowOverride All,WordPress 会通过 .htaccess 自动添加重写规则。

一台 Nginx 服务器能托管多少个网站?有数量限制吗?

Nginx 本身没有站点数量上限,每个站点对应 conf.d/ 下的一个 .conf 文件,Nginx 启动时全部加载到内存。实际限制来自服务器资源:① 内存是主要瓶颈——每个站点的 PHP-FPM 进程池、MySQL 连接占用内存,一台 2GB 的服务器托管 5-10 个中低流量 PHP 站点完全没问题;② 磁盘 I/O:大量站点同时有访问时,磁盘读写可能成为瓶颈;③ 数据库连接数:MySQL 默认最大连接数 151,多站点共用时可能不够。经验值:2GB 内存的 VPS 稳定托管 5-8 个 WordPress,4GB 可以托管 15-20 个低流量站点。

Caddy 的自动证书存储在哪里?能导出给 Nginx 用吗?

Caddy 将证书存储在 /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/(或 ~/.local/share/caddy/,取决于运行用户)。证书文件是标准的 PEM 格式,技术上可以复制给 Nginx 用,但不推荐这种做法——Caddy 自动续期后文件路径不变,但 Nginx 不会自动 reload,导致 Nginx 仍用旧证书。更好的做法:如果您需要 Nginx,直接用 Certbot 或 acme.sh 为 Nginx 申请证书;或者让 Caddy 作为前端代理,Nginx 在后端只处理逻辑,不直接面向公网。

学完 Web 服务器后,下一步应该怎么进阶?

按本站 30 篇路径,第 14 篇(本篇)→ 第 15 篇(docker-deployment)→ 第 16 篇(database-setup)是最紧密的进阶链条。关联逻辑:Docker 和 Nginx 是目前最流行的部署组合——Docker 管理应用容器,Nginx 作为前端反向代理统一对外服务;掌握了本篇的反向代理配置,在第 15 篇中把 proxy_pass 指向 Docker 容器就能实现完整的容器化部署。第 16 篇的数据库搭建则为您的 Web 应用提供数据存储能力,Nginx + PHP-FPM + MySQL 三件套构成了传统 LEMP 栈的全部组件。

PHP-FPM 的 pm.max_children 设置太小会有什么表现?如何判断需要调大?

当并发请求超过 pm.max_children 时,多余的请求会排队等待,表现为:① 网站响应时间突然变慢(从 0.1s 变成 3-5s);② Nginx 的 access.log 中出现大量 499(客户端等待超时后主动断开)或 502;③ /var/log/php8.3-fpm.log 中出现 WARNING: [pool www] server reached pm.max_children, consider raising it 这行警告——这是最直接的信号。调整公式max_children = 可用物理内存 MB / 单个 PHP 进程内存 MB。查看单进程内存:ps --no-headers -o "rss,cmd" -C php-fpm8.3 | awk '{sum+=$1} END {print sum/NR/1024 "MB per process"}'。WordPress 通常每进程 50-80MB,1GB 内存(留 400MB 给系统和 MySQL)可设 max_children 约 15-20。