虽然 LAMP 堆栈(Linux + Apache + MySQL + PHP)在支撑 WordPress 方面非常流行,但也可以使用 Nginx。WordPress 支持 Nginx,一些大型 WordPress 站点(如 WordPress.com)就是由 Nginx 驱动的。

在谈论 Nginx 时,重要的是要知道实现 Nginx 有多种方式。它可以配置为 Apache 前面的反向代理,这是一种非常强大的设置,允许您使用 Apache 的所有功能和优势,同时受益于 Nginx 的速度。大多数报告使用 Nginx 作为服务器(基于从 HTTP 响应头收集的统计数据)的网站,实际上是在运行 Apache,并以 Nginx 作为反向代理。(显示"Nginx"的 HTTP 响应头是由反向代理报告的,而不是服务器本身。)

本指南指的是独立的 Nginx 设置,即将其用作主服务器而不是 Apache。 需要注意的是,Nginx 并不是 Apache 的完全可互换替代品。在您继续之前,有几个影响 WordPress 实施的关键差异需要注意:

  • 使用 Nginx 时,没有像 Apache 的 .htaccess 或 IIS 的 web.config 那样的目录级配置文件。所有配置必须由管理员在服务器级别完成,而 WordPress 无法修改配置,就像它可以在 Apache 或 IIS 上那样。
  • 在使用 Nginx 运行时,Pretty Permalinks 功能略有不同。
  • 由于 Nginx 不具备 .htaccess 类型的能力,且 WordPress 无法自动为您修改服务器配置,因此它也无法为您生成重写规则。
  • 如果不修改您的安装,"index.php"将添加到您的 Permalinks 中。(可以通过插件(见下文)和/或在子主题的 functions.php 中添加自定义代码来缓解这个问题。)
  • 但是,如果您确实想要一些(有限的).htaccess 能力,从技术上讲,通过安装 PHP 的 htscanner PECL 扩展 是可以实现的。(然而,这不是一个完美的解决方案,因此在用于生产站点之前请务必进行彻底测试和调试。)

本指南不会涵盖如何安装和配置 Nginx,因此假设您已经安装了 Nginx,并且对如何使用和调试它有基本的了解。

通用和多站点支持

为了让 WordPress 与 Nginx 配合工作,您需要配置后端的 php-cgi。可用的选项是 fastcgi 或 php-fpm。这里使用 php-fpm,因为它包含在 PHP 5.3+ 中,所以安装很简单。

Nginx 配置已被拆分为五个独立文件,并进行了大量注释,以便更容易理解每个选项。作者也尽力尝试遵循 nginx 配置的“最佳实践”。

主(通用)启动文件

这相当于 /etc/nginx/nginx.conf(如果您使用的是 Arch Linux,则为 /etc/nginx/conf/nginx.conf)。

# Generic startup file.
user {user} {group};

#usually equal to number of CPUs you have. run command "grep processor /proc/cpuinfo | wc -l" to find it
worker_processes  auto;
worker_cpu_affinity auto;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

# Keeps the logs free of messages about not being able to bind().
#daemon     off;

events {
    worker_connections  1024;
}

http {
    #rewrite_log on;

    include mime.types;
    default_type       application/octet-stream;
    access_log         /var/log/nginx/access.log;
    sendfile           on;
    #tcp_nopush         on;
    keepalive_timeout  3;
    #tcp_nodelay        on;
    #gzip               on;
    #php max upload limit cannot be larger than this
    client_max_body_size 13m;
    index              index.php index.html index.htm;

    # Upstream to abstract backend connection(s) for PHP.
    upstream php {
        #this should match value of "listen" directive in php-fpm pool
        server unix:/tmp/php-fpm.sock;
        # server 127.0.0.1:9000;
    }

    include sites-enabled/*;
}

每站点配置

# Redirect everything to the main site. We use a separate server statement and NOT an if statement - see https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/

server {
    server_name  _;
    return 302 $scheme://example.com$request_uri;
}

server {
    server_name example.com;
    root /var/www/example.com;

    index index.php;

    include global/restrictions.conf;

    # Additional rules go here.

    # Only include one of the files below.
    include global/wordpress.conf;
    # include global/wordpress-ms-subdir.conf;
    # include global/wordpress-ms-subdomain.conf;
}

将配置的各个部分拆分为多个文件允许重复使用相同的逻辑。使用'global'子目录来添加额外规则以供通用用途使用(根据您的 nginx 安装设置,位于 /etc/nginx/conf/global/ 或 /etc/nginx/global/)。

全局限制文件

# Global restrictions configuration file.
# Designed to be included in any server {} block.
location = /favicon.ico {
    log_not_found off;
    access_log off;
}

location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
}

# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
    deny all;
}

# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~* /(?:uploads|files)/.*\.php$ {
    deny all;
}

通用 WordPress 规则

对于单站点安装,这里是 global/wordpress.conf 文件:

# WordPress single site rules.
# Designed to be included in any server {} block.
# Upstream to abstract backend connection(s) for php
upstream php {
    server unix:/tmp/php-cgi.socket;
    server 127.0.0.1:9000;
}

server {
    ## Your website name goes here.
    server_name domain.tld;
    ## Your only path reference.
    root /var/www/wordpress;
    ## This should be in your http block and if it is, it's not needed here.
    index index.php;

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location / {
        # This is cool because no php is touched for static content.
        # include the "?$args" part so non-default permalinks doesn't break when using query string
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
        include fastcgi.conf;
        fastcgi_intercept_errors on;
        fastcgi_pass php;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
    }
}

这是更更新的 Nginx 示例:https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/

WordPress 多站点

对于多站点安装,根据启用多站点时使用的 WordPress 版本以及域名/子目录配置,在 global/wordpress.conf 文件中使用以下其中一个部分。

WordPress 3.5 及以上版本

如果您在 WordPress 3.5 或更高版本上启用了 Multisite,请使用其中之一。

WordPress 3.5 及以上版本子目录示例
# WordPress multisite subdirectory config file for WP 3.5 and up.
server {
    server_name example.com ;

    root /var/www/example.com/htdocs;
    index index.php;

    if (!-e $request_filename) {
        rewrite /wp-admin$ $scheme://$host$request_uri/ permanent;
        rewrite ^(/[^/]+)?(/wp-.*) $2 last;
        rewrite ^(/[^/]+)?(/.*\.php) $2 last;
    }

    location / {
        try_files $uri $uri/ /index.php?$args ;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass php;
    }

    #add some rules for static content expiry-headers here
}
WordPress 3.5 及以上版本子域名示例
# WordPress multisite subdomain config file for WP 3.5 and up.
server {
    server_name example.com *.example.com ;

    root /var/www/example.com/htdocs;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$args ;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass php;
    }

    #add some rules for static content expiry-headers here
}

WordPress 3.4 及以下版本

如果您最初是使用 WordPress 3.4 或更早版本启用 Multisite 的,则需要使用其中之一:

WordPress <=3.4 子目录示例
# WordPress multisite subdirectory config file for WP 3.4 and below.

map $uri $blogname{
    ~^(?P<blogpath>/[^/]+/)files/(.*)       $blogpath ;
}

map $blogname $blogid{
    default -999;

    #Ref: https://wordpress.org/extend/plugins/nginx-helper/
    #include /var/www/wordpress/wp-content/plugins/nginx-helper/map.conf ;
}

server {
    server_name example.com ;

    root /var/www/example.com/htdocs;
    index index.php;

    location ~ ^(/[^/]+/)?files/(.+) {
        try_files /wp-content/blogs.dir/$blogid/files/$2 /wp-includes/ms-files.php?file=$2 ;
        access_log off;     log_not_found off; expires max;
    }

    #avoid php readfile()
    location ^~ /blogs.dir {
        internal;
        alias /var/www/example.com/htdocs/wp-content/blogs.dir ;
        access_log off;     log_not_found off; expires max;
    }

    if (!-e $request_filename) {
        rewrite /wp-admin$ $scheme://$host$request_uri/ permanent;
        rewrite ^(/[^/]+)?(/wp-.*) $2 last;
        rewrite ^(/[^/]+)?(/.*\.php) $2 last;
    }

    location / {
        try_files $uri $uri/ /index.php?$args ;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass php;
    }

    #add some rules for static content expiry-headers here
}

NGINX 提供了两个特殊指令:X-Accel-Redirect 和 map。使用这两个指令,可以消除 WordPress 多站点网络中静态文件服务的性能影响。

WordPress <=3.4 子域名示例
# WordPress multisite subdomain config file for WP 3.4 and below.
map $http_host $blogid {
    default       -999;

    #Ref: https://wordpress.org/extend/plugins/nginx-helper/
    #include /var/www/wordpress/wp-content/plugins/nginx-helper/map.conf ;

}

server {
    server_name example.com *.example.com ;

    root /var/www/example.com/htdocs;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$args ;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass php;
    }

    #WPMU Files
        location ~ ^/files/(.*)$ {
            try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?file=$1 ;
            access_log off; log_not_found off; expires max;
        }

    #WPMU x-sendfile to avoid php readfile()
    location ^~ /blogs.dir {
        internal;
        alias /var/www/example.com/htdocs/wp-content/blogs.dir;
        access_log off;     log_not_found off;      expires max;
    }

    #add some rules for static content expiry-headers here
}

参考:https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/

Nginx 中的 HTTPS

在 Nginx 中启用 HTTPS 相对简单。

server {
    # listens both on IPv4 and IPv6 on 443 and enables HTTPS and HTTP/2 support.
    # HTTP/2 is available in nginx 1.9.5 and above.
    listen *:443 ssl;
    listen [::]:443 ssl;
    http2 on;

    # indicate locations of SSL key files.
    ssl_certificate /srv/www/ssl/ssl.crt;
    ssl_certificate_key /srv/www/ssl/ssl.key;
    ssl_dhparam /srv/www/master/ssl/dhparam.pem;

    # indicate the server name
    server_name example.com *.example.com;

    # Enable HSTS. This forces SSL on clients that respect it, most modern browsers. The includeSubDomains flag is optional.
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

    # Set caches, protocols, and accepted ciphers. This config will merit an A+ SSL Labs score as of Sept 2015.
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5';
}

Mozilla 也提供了一个优秀的 SSL 配置生成工具。

WP Super Cache 规则

# WP Super Cache rules.
# Designed to be included from a 'wordpress-ms-...' configuration file.

set $cache_uri $request_uri;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $cache_uri 'null cache';
}

if ($query_string != "") {
    set $cache_uri 'null cache';
}

# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
}

# START MOBILE
# Mobile browsers section to server them non-cached version. COMMENTED by default as most modern wordpress themes including twenty-eleven are responsive. Uncomment config lines in this section if you want to use a plugin like WP-Touch
# if ($http_x_wap_profile) {
    # set $cache_uri 'null cache';
#}

#if ($http_profile) {
    # set $cache_uri 'null cache';
#}

#if ($http_user_agent ~* (2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800)) {
    # set $cache_uri 'null cache';
#}

#if ($http_user_agent ~* (w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-)) {
    # set $cache_uri 'null cache';
#}
#END MOBILE

# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
    try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php?$args ;
}

实验性修改:

如果您正在使用 HTTPS,WP Super Cache 的最新开发版本可能会使用不同的目录结构来区分 HTTP 和 HTTPS。try_files 行可能如下所示:

location / {
    try_files /wp-content/cache/supercache/$http_host/$cache_uri/index-https.html $uri $uri/ /index.php?$args ;
}

W3 Total Cache 规则

W3 Total Cache 根据 WordPress 配置为基于磁盘的缓存存储使用不同的目录结构。

缓存验证检查将保持如下所示的通用性:

#W3 TOTAL CACHE CHECK
set $cache_uri $request_uri;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $cache_uri 'null cache';
}
if ($query_string != "") {
    set $cache_uri 'null cache';
}

# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
}
#ADD mobile rules from WP SUPER CACHE section above

#APPEND A CODE BLOCK FROM BELOW...

对于普通 WordPress(无多站点)

请使用以下内容:

# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
    try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args ;
}

对于具有子目录的多站点
请使用以下内容:

if ( $request_uri ~* "^/([_0-9a-zA-Z-]+)/.*" ){
    set $blog $1;
}

set $blog "${blog}.";

if ( $blog = "blog." ){
    set $blog "";
}

# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
    try_files /wp-content/w3tc-$blog$host/pgcache$cache_uri/_index.html $uri $uri/ /index.php?$args ;
}

对于具有子域名/域映射的多站点
请使用以下内容:

location / {
    try_files /wp-content/w3tc-$host/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args;
}

注意事项

  • Nginx 可以自动处理 gzip 和浏览器缓存,因此最好将这部分留给 Nginx 处理。
  • W3 Total Cache Minify 规则可以与上述配置一起使用,不会有任何问题。

Nginx fastcgi_cache

Nginx 可以在其端进行缓存以减少服务器负载。当您想使用 Nginx 内置的 fastcgi_cache 时,最好使用 fastcgi_cache_purge 模块编译 Nginx。这将帮助 Nginx 在页面被编辑时清除该页面的缓存。在 WordPress 端,您需要安装一个插件,例如 Nginx Helper,以利用 fastcgi_cache_purge 功能。

配置将如下所示:

在 http{…} 块中定义 Nginx 缓存区域,位于 server{…} 块之外

#move next 3 lines to /etc/nginx/nginx.conf if you want to use fastcgi_cache across many sites
fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:500m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;

对于 WordPress 站点配置,在 server{..} 块中添加如下缓存检查块

#fastcgi_cache start
set $no_cache 0;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $no_cache 1;
}
if ($query_string != "") {
    set $no_cache 1;
}

# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
        set $no_cache 1;
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
        set $no_cache 1;
}

然后修改 PHP 处理块

只需将此添加到下面的 php 块中。注意行 fastcgi_cache_valid 200 60m;,它告诉 Nginx 仅缓存 200 响应(正常页面),这意味着重定向不会被缓存。这对于多语言站点非常重要,如果未实施此配置,Nginx 将缓存一种语言的主 URL,而不是根据用户的语言将其重定向到相应内容。

fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;

fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;

使其变为如下形式:

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }
    # This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default)

    include fastcgi.conf;
    fastcgi_index index.php;
    # fastcgi_intercept_errors on;
    fastcgi_pass php;

    fastcgi_cache_bypass $no_cache;
    fastcgi_no_cache $no_cache;

    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 200 60m;
}

最后添加一个用于条件清除的位置块

location ~ /purge(/.*) {
    # Uncomment the following two lines to allow purge only from the webserver
    # allow 127.0.0.1;
    # deny all;

    fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
}

如果您收到“未知指令 'fastcgi_cache_purge'"错误,请检查您的 Nginx 安装是否包含 fastcgi_cache_purge 模块。

多站点静态文件的更好性能 (WP <= 3.4)

默认情况下,在 3.5 之前激活的多站点网络中,静态文件请求会引入 PHP,即 ms-files.php 文件。使用 Nginx Map{..} 指令可以获得更好的性能。

在您站点的 Nginx 配置中,位于 server{..} 块上方,添加如下部分:

map $http_host $blogid {
    default               0;

    example.com           1;
    site1.example.com     2;
    site1.com             2;
}

这只是一个站点名称和博客 ID 的列表。您可以使用 Nginx helper 获取此类站点名称/博客 ID 对列表。该插件还将生成一个 map.conf 文件,您可以直接将其包含在 map{} 部分中,如下所示:

map $http_host $blogid {
    default               0;

    include /path/to/map.conf ;
}

创建 map{..} 部分后,您只需在 Nginx 配置中进行一项更改,以便对 /files/ 的请求首先使用 Nginx map{..} 进行处理:

location ~ ^/files/(.*)$ {
    try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?file=$1 ;
    access_log off; log_not_found off; expires max;
}

注意事项

  • 每当创建新站点、删除站点或将额外域名映射到现有站点时,Nginx helper 都会自动更新 map.conf 文件,但您仍需手动重新加载 Nginx 配置。您可以在稍后的任何时间执行此操作。在此之前,只有新站点的文件将通过 php-fpm 提供。
  • 此方法不会生成任何符号链接。因此,不会出现意外删除或跟随符号链接的备份脚本导致的问题。
  • 对于大型网络,这将很好地扩展,因为只有一个 map.conf 文件。

最后有两点重要说明:整个设置假设站点根目录即为博客,且所有引用的文件都驻留在主机上。如果您将博客放在子目录中(例如 /blog),则规则需要进行修改。也许有人可以利用这些规则,例如在主'server'块中使用:

set $wp_subdir "/blog";

指令,并使其自动应用于通用 WP 规则。

警告

全局限制文件 中的拼写错误可能会产生漏洞。要测试您的"uploads"目录是否真正受到保护,请创建一个包含某些内容的 PHP 文件(示例:),将其上传到"uploads"目录(或其子目录之一),然后尝试从浏览器访问(执行)它。

资源

参考

外部链接

脚本与工具

对于 WordPress Nginx 脚本化安装,CentminMod 可用于 CentOS。

securing Nginx