Skip to content

Nginx + PHP-FPM 配置

Nginx 与 PHP-FPM 的组合是 PHP 应用最常见的部署架构。Nginx 作为反向代理和静态文件服务器,负责处理 HTTP 请求、负载均衡和缓存;PHP-FPM 作为 PHP 进程管理器,负责执行 PHP 代码。本节将详细讲解 Nginx 与 PHP-FPM 的集成配置、优化参数、负载均衡、缓存配置以及 HTTPS 设置。

前置知识

阅读本节前,建议先了解:PHP-FPM 调优Docker 部署

基础概念

Nginx + PHP-FPM 通信方式

text
方式一:Unix Socket(推荐)
  Client → Nginx → unix:/var/run/php-fpm/www.sock → PHP-FPM

方式二:TCP Socket
  Client → Nginx → 127.0.0.1:9000 → PHP-FPM

方式三:远程 TCP
  Client → Nginx → 10.0.0.1:9000 → PHP-FPM (远程服务器)

基础配置

fastcgi_params 配置

nginx
# /etc/nginx/fastcgi_params
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

fastcgi_param  REDIRECT_STATUS    200;

基本 Server 配置

nginx
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/app/public;
    index index.php index.html;

    charset utf-8;

    # 安全头部
    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;

    # 禁止访问隐藏文件
    location ~ /\.(?!well-known) {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 主要路由
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

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

        # FastCGI 优化
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
        fastcgi_busy_buffers_size 32k;
        fastcgi_read_timeout 300;
        fastcgi_intercept_errors on;
    }

    # 静态文件缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2?|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
        log_not_found off;
    }

    # 健康检查端点
    location = /health {
        access_log off;
        return 200 "OK";
        add_header Content-Type text/plain;
    }

    # 访问日志和错误日志
    access_log /var/log/nginx/app_access.log;
    error_log  /var/log/nginx/app_error.log;
}

Upstream 与负载均衡

多个 PHP-FPM 实例负载均衡

nginx
# Upstream 定义
upstream php_backend {
    # 加权轮询(默认策略)
    server unix:/var/run/php-fpm/app_1.sock weight=3;
    server unix:/var/run/php-fpm/app_2.sock weight=2;
    server unix:/var/run/php-fpm/app_3.sock weight=1;

    # 备用服务器
    # server unix:/var/run/php-fpm/app_backup.sock backup;

    # 健康检查参数
    keepalive 32;
    keepalive_timeout 60s;
}

# 最少连接策略
upstream php_backend_least_conn {
    least_conn;
    server 10.0.0.1:9000;
    server 10.0.0.2:9000;
    server 10.0.0.3:9000;
}

# IP Hash 策略(会话保持)
upstream php_backend_ip_hash {
    ip_hash;
    server 10.0.0.1:9000;
    server 10.0.0.2:9000;
}

server {
    listen 80;
    server_name example.com;

    location ~ \.php$ {
        fastcgi_pass php_backend;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Keepalive 连接复用

nginx
upstream php_fpm {
    server unix:/var/run/php-fpm/www.sock;
    keepalive 32;  # 每个 worker 的空闲 keepalive 连接数
}

server {
    location ~ \.php$ {
        fastcgi_pass php_fpm;
        fastcgi_keep_conn on;  # 启用 keepalive
        include fastcgi_params;
    }
}

缓存配置

FastCGI 缓存

nginx
# 定义缓存区域
fastcgi_cache_path /var/cache/nginx/fastcgi
    levels=1:2
    keys_zone=PHP_CACHE:100m
    max_size=1g
    inactive=60m
    use_temp_path=off;

server {
    listen 80;
    server_name example.com;
    root /var/www/app/public;

    # 启用 FastCGI 缓存
    set $skip_cache 0;

    # POST 请求不缓存
    if ($request_method = POST) {
        set $skip_cache 1;
    }

    # 带查询参数的请求不缓存
    if ($query_string != "") {
        set $skip_cache 1;
    }

    # 带有特定 Cookie 的请求不缓存
    if ($http_cookie ~* "session_id|auth_token") {
        set $skip_cache 1;
    }

    # 登录用户不缓存
    if ($request_uri ~* "/admin|/login|/api") {
        set $skip_cache 1;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/www.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # 缓存配置
        fastcgi_cache           PHP_CACHE;
        fastcgi_cache_valid     200 301 302 10m;
        fastcgi_cache_valid     404 1m;
        fastcgi_cache_bypass    $skip_cache;
        fastcgi_no_cache        $skip_cache;
        fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
        fastcgi_cache_key       "$scheme$request_method$host$request_uri";
        fastcgi_cache_lock      on;
        fastcgi_cache_lock_timeout 5s;

        # 缓存状态头(调试用)
        add_header X-FastCGI-Cache $upstream_cache_status;
    }
}

HTTPS 配置

Let's Encrypt SSL 配置

nginx
# HTTP → HTTPS 重定向
server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS 配置
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    root /var/www/app/public;

    # SSL 证书
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # SSL 优化参数
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    # 其余配置...
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/www.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_param HTTPS on;
    }
}

性能优化

全局优化配置

nginx
# /etc/nginx/nginx.conf

user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

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" "$http_x_forwarded_for" '
                    'rt=$request_time';

    # 基础优化
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000;
    types_hash_max_size 2048;
    server_tokens off;

    # Gzip 压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_types
        application/atom+xml
        application/geo+json
        application/javascript
        application/x-javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rdf+xml
        application/rss+xml
        application/xhtml+xml
        application/xml
        font/eot
        font/otf
        font/ttf
        image/svg+xml
        text/css
        text/javascript
        text/plain
        text/xml;

    # 包含站点配置
    include /etc/nginx/conf.d/*.conf;
}

实战示例

API 专用 Nginx 配置

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

    root /var/www/app/public;

    # 速率限制
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/m;

    # 客户端请求体大小限制
    client_max_body_size 20m;

    # API 路由
    location / {
        limit_req zone=api_limit burst=20 nodelay;
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        limit_req zone=api_limit burst=20 nodelay;
        fastcgi_pass unix:/var/run/php-fpm/www.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # API 超时设置
        fastcgi_read_timeout 60s;
        fastcgi_send_timeout 60s;
        fastcgi_buffer_size 32k;
        fastcgi_buffers 8 32k;
    }

    access_log /var/log/nginx/api_access.log main;
}

注意事项

安全配置

  • 禁用 server_tokens 防止版本泄露
  • 配置合理的 client_max_body_size
  • 设置 security headers(X-Frame-Options、CSP 等)
  • 限制访问敏感目录(.env、storage 等)

最佳实践

  1. 使用 Unix Socket:比 TCP 性能更好
  2. 启用 Keepalive:减少连接建立开销
  3. 开启 Gzip:减少传输数据量
  4. 配置 FastCGI 缓存:减少 PHP 执行次数
  5. 设置合理的超时:避免慢请求阻塞 worker
  6. 分离静态文件:Nginx 直接处理静态文件,不走 PHP
  7. 日志分析:使用合适的日志格式便于后续分析

下一节

继续学习:日志管理

参考链接