Nginx - Arch Linux 中文维基

跳至內容

# Nginx

出自 Arch Linux 中文维基
翻譯狀態：

- 本文（或部分內容）譯自 Nginx，最近一次同步於 2025-02-12，若英文版本有所更改，則您可以幫助同步與翻譯更改的內容。
- 您可以在 ArchWiki 的對應頁面找到本文翻譯的原始修訂歷史。
- 本文可能與英文原文存在出入。

Nginx (讀作"engine X") 由Igor Sysoev(俄羅斯)於2005年編寫，是一個免費、開源、高性能的HTTP web伺服器和反向代理，也可以作為一個IMAP/POP3代理伺服器。Nginx因為穩定，豐富的功能集，配置簡單，資源占用低而聞名世界。

這篇文章描述了如何設置nginx並且如何通過#FastCGI集成PHP.

## 安裝

安裝以下其中一個軟體包：

- nginx-mainline包 - 主線分支：新功能、更新、錯誤修復。
- nginx包 - 穩定分支：僅重大錯誤修復。
- angieAUR - nginx 的分支和直接替代品，具有更多功能。
- freenginx-mainlineAUR - 保留 nginx 自由和開放開發的直接替代品（主線分支）。
- freenginx-libresslAUR - 保留 nginx 自由和開放開發的直接替代品（主線分支，支持 LibreSSL）。
- freenginxAUR - 保留 nginx 自由和開放開發的直接替代品（穩定分支）。

推薦使用主線分支。使用穩定分支的主要原因是擔心新功能可能帶來的不良影響，例如與第三方模塊不兼容或開發者在引入新功能時疏忽導致的錯誤。

注意：所有在official repositories可用的模塊都需要nginx（與nginx-mainline相反）作為依賴。你可能需要在下決定選nginx還是nginx-mainline前，先查看下模塊名單。nginx-mainline的模塊能在Arch User Repository找到。

對於在基於chroot環境下的額外安全性，可查閱#在chroot環境下安裝。

## 啟動

啟動/啟用 `nginx.service` 或 `angie.service`（如果你使用 Angie）。

默認在 http://127.0.0.1 頁面服務的頁面是 `/usr/share/nginx/html/index.html`。

## 配置

安裝nginx後的第一步該幹什麼寫在用戶手冊裡了。你可以通過編輯在`/etc/nginx/`下的文件來修改配置。主配置文件在`/etc/nginx/nginx.conf`。

更多細節和例子，你可以在官方文檔找到。

下面的例子包含了最常見的使用案例。我們假定你使用的是默認文件路徑(`/usr/share/nginx/html`)。如果你改了路徑，用你自己的路徑替代。

提示：DigitalOcean 提供了一個 Nginx 配置工具。

### 配置例子

    /etc/nginx/nginx.conf

    user http;
    worker_processes auto;
    worker_cpu_affinity auto;

    events {
        worker_connections 1024;
    }

    http {
        charset utf-8;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        server_tokens off;
        log_not_found off;
        types_hash_max_size 4096;
        client_max_body_size 16M;

        # MIME
        include mime.types;
        default_type application/octet-stream;

        # logging
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log warn;

        # load configs
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
    }

### 通用配置

#### 進程和連接

你應該為`worker_processes`選一個合適的值。這項設置最終決定了nginx接受多少連接和它會使用多少處理器。通常來說，把它設置成你系統裡的硬體線程數就好了。可選的是，自從 1.3.8 和 1.2.5版本以後， `worker_processes` 接受 `auto` 作為值了，它會自動檢測最優值 (source)。

nginx的最大連接數有下面的公式給出`max_clients = worker_processes * worker_connections`。

#### 不同用戶間的使用

默認的，nginx包 用 `root` 身份運行主進程而用`http`用戶運行worker進程。如果要用其它用戶運行worker進程，改變`nginx.conf`裡的`user`參數就好了：

    /etc/nginx/nginx.conf

    user user [group];

如果組（group）忽略不寫的話，就會用和user相同的名字來代替。

提示：其實也可以使用systemd以`root`身份在沒有任何東西運行的情況下運行nginx。可以看 #使用 systemd無權限運行 和 #使用 systemd 用戶服務運行。

#### server代碼塊

通過使用 `server` 模塊，可以實現服務多個域名。這些模塊可以類比為Apache HTTP Server中的"VirtualHosts"。也可查閱上游文獻。

下面的例子，伺服器監聽IPv4和IPv6的80埠的進入流量的兩個域名，分別是`domainname1.tld` 和`domainname2.tld`：

    /etc/nginx/nginx.conf

    ...
    server {
        listen 80;
        listen [::]:80;
        server_name domainname1.tld;
        root /usr/share/nginx/domainname1.tld/html;
        location / {
            index index.php index.html index.htm;
        }
    }

    server {
        listen 80;
        listen [::]:80;
        server_name domainname2.tld;
        root /usr/share/nginx/domainname2.tld/html;
        ...
    }

Restart(重啟) `nginx.service` 來讓改變的配置生效。

確保主機名是可以被設置好的DNS伺服器比如BIND 或者 dnsmasq解析的，或者可以查看下網絡配置#Local network hostname resolution。

##### 管理伺服器入口

把不同的`server`模塊放到不同的文件裡是可能的。這樣的話可以讓你很輕易的開啟和禁用特定的站點。

本文或本章節的事實準確性存在爭議。

原因： 有人認為使用 `sites-enabled` 和 `sites-available` 的方法已經不再有用，反而會帶來更多問題，可以參考 比較這兩種方法 和 使用這種方法可能引發的問題。

相反，可以直接在 `/etc/nginx/conf.d/` 目錄下創建文件，這種方式符合標準的配置文件放置方式。然後在主配置文件中加入 `include /etc/nginx/conf.d/*.conf`，類似於包含其他目錄中的文件模式。這樣，可以通過將文件重命名為 `original_name.conf.disabled` 來禁用站點，因為只有以 .conf 結尾的文件才會被包含。

（在 Talk:Nginx 中討論）

如果仍然想使用 `sites-enabled` 和 `sites-available` 的方法，可以創建以下文件夾：

    # mkdir /etc/nginx/sites-available
    # mkdir /etc/nginx/sites-enabled

在`sites-available`文件夾下創建一個文件包含一個或更多伺服器模塊：

    /etc/nginx/sites-available/example.conf

    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        http2 on;

        ...
    }

把`include sites-enabled/*;`放到`http` 模塊的末尾：

    /etc/nginx/nginx.conf

    http {
        ...
        include sites-enabled/*;
    }

如果要啟用一個網站，只需要簡單的創建一個符號連結：

    # ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.conf

如果要移除一個網站：

    # unlink /etc/nginx/sites-enabled/example.conf

Reload/restart `nginx.service` 來讓配置生效。

#### TLS

本文或本章節的語言、語法或風格需要改進。參考：幫助:風格

原因：不要僅僅把 OpenSSL#Usage 的配置搬過來。（在Talk:Nginx討論）

OpenSSL 提供了TLS支持並且默認在Arch系統安裝時安裝了。

提示：

- 你可能需要在配置SSL之前閱讀 ngx_http_ssl_module 文檔。
- Let’s Encrypt 是一個免費的、自動的、開放的證書頒發機構。有一個插件可以從命令行請求有效的證書並自動配置。
- Mozilla有一篇有用的 TLS文章，也有一個自動化工具來創建一個更安全的配置。

警告：如果你打算實現TLS，你必須知道一些變化和實現 仍然被攻擊時很脆弱[1]。如果想知道TLS內現存的漏洞和怎樣用合適的方法到nginx上，可訪問 https://weakdh.org/sysadmin.html

創建一個私鑰和自簽名的證書，這對大多數不需要 CSR的安裝來說足夠了：

    # mkdir /etc/nginx/ssl
    # cd /etc/nginx/ssl
    # openssl req -new -x509 -nodes -newkey rsa:4096 -keyout server.key -out server.crt -days 1095
    # chmod 400 server.key
    # chmod 444 server.crt

注意：`-days` 是可選的並且RSA鍵大小最低可到2048 (默認值)。

如果你需要創建一個CSR，用下面的教程而不是上面的：

    # mkdir /etc/nginx/ssl
    # cd /etc/nginx/ssl
    # openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out server.key
    # chmod 400 server.key
    # openssl req -new -sha256 -key server.key -out server.csr
    # openssl x509 -req -days 1095 -in server.csr -signkey server.key -out server.crt

注意：如果你需要更多的 openssl選項，可以閱讀 openssl(1ssl) 或擴展文檔。

使用TLS的 `/etc/nginx/nginx.conf` 的基本例子可以參考 Mozilla的SSL配置生成器。

Restart `nginx.service` 來讓配置生效。

#### 分用戶目錄

如果要複製Apache式的 `~user` URLs 到用戶的 `~/public_html` 目錄，可以嘗試下面的。(注意：如果下面的兩個規則都用了的話一定要把更清楚地PHP規則放在前面。)

    /etc/nginx/nginx.conf

    ...
    server {
        ...
        # PHP in user directories, e.g. http://example.com/~user/test.php
        location ~ ^/~(.+?)(/.+\.php)$ {
            alias          /home/$1/public_html$2;
            fastcgi_pass   unix:/run/php-fpm/php-fpm.sock;
            fastcgi_index  index.php;
            include        fastcgi.conf;
        }

        # User directories, e.g. http://example.com/~user/
        location ~ ^/~(.+?)(/.*)?$ {
            alias     /home/$1/public_html$2;
            index     index.html index.htm;
            autoindex on;
        }
        ...
    }
    ...

查閱 #PHP實現來閱讀更多 `nginx`的PHP配置。

Restart `nginx.service` 來啟用新配置。

### FastCGI

FastCGI, 也叫 FCGI, 是適用於web伺服器和交互式程序的接口的協議. FastCGI 是早期的 Common Gateway Interface (CGI)的變種; FastCGI的主要目的是減少CGI程序和web伺服器交互的開銷,來允許伺服器同時處理更多網頁請求.

FastCGI技術被引進nginx是為了與許多外部工具，比如. Perl, PHP 和 Python

#### PHP實現

PHP-FPM 是建議使用的用作PHP的FastCGI伺服器的解決方案.

安裝 php-fpm包 然後確保 PHP 被安裝配置正確. PHP-FPM的主要配置文件是 `/etc/php/php-fpm.conf`. 基礎使用的話默認配置就足夠了.

最後, 啟動/啟用 `php-fpm.service`.

你也可以使用 php-legacy-fpm包 代替，參見 #使用 php-legacy.

注意：

- 如果你 用不同的用戶運行nginx, 確保PHP-FPM套接字文件能被這個用戶訪問，或者通過一個TCP套接字.
- 如果你在chroot的環境下運行nginx (chroot 是 `/srv/nginx-jail`, web頁面在 `/srv/nginx-jail/www`), 你必須修改文件 `/etc/php/php-fpm.conf` ，把 `chroot = /srv/nginx-jail` 和 `listen = /srv/nginx-jail/run/php-fpm/php-fpm.sock` 兩行加入到指令部分 (默認是`[www]`). 如果缺少的話請為套接字文件創建目錄。此外, 動態連結到依賴的模塊，你需要將這些依賴複製到(比如. 對於php-imagick, 你需要複製ImageMagic的庫複製到chroot, 而不是imagick.so他自己).

##### nginx 配置

當服務一個PHP web程序時，一個PHP-FPM的`location`應該包括在 #server代碼塊裡 [2],比如.:

    /etc/nginx/sites-available/example.conf

    server {
        root /usr/share/nginx/html;

        location / {
            index index.html index.htm index.php;
        }

        location ~ \.php$ {
            # 404
            try_files $fastcgi_script_name =404;

            # default fastcgi_params
            include fastcgi_params;

            # fastcgi settings
            fastcgi_pass			unix:/run/php-fpm/php-fpm.sock;
            fastcgi_index			index.php;
            fastcgi_buffers			8 16k;
            fastcgi_buffer_size		32k;

            # fastcgi params
            fastcgi_param DOCUMENT_ROOT	$realpath_root;
            fastcgi_param SCRIPT_FILENAME	$realpath_root$fastcgi_script_name;
            #fastcgi_param PHP_ADMIN_VALUE	"open_basedir=$base/:/usr/lib/php/:/tmp/";
        }
    }

如果需要用PHP處理其他文件擴展名 (比如. .html and .htm):

    location ~ [^/]\.(php|html|htm)(/|$) {
        ...
    }

非 .php 的PHP-FPM擴展處理都需要被直接加入到 `/etc/php/php-fpm.d/www.conf`:

    security.limit_extensions = .php .html .htm

注意：需要注意下 `fastcgi_pass` 參數, 因為它被選定的FastCGI伺服器在它的配置文件裡定義TCP或Unix套接字. 對應`php-fpm`的default (Unix) 套接字是 是:

    fastcgi_pass unix:/run/php-fpm/php-fpm.sock;

你可能想要使用常見的 TCP 套接字, not default,

    fastcgi_pass 127.0.0.1:9000;

Unix 的域名套接字應該會快一點.
提示：如果你使用多個 `server` 塊來啟用PHP支持, 創建一個 `php_fastcgi.conf` 配置文件可能更簡單:

    /etc/nginx/php_fastcgi.conf

    location ~ \.php$ {
        # 404
        try_files $fastcgi_script_name =404;
        # default fastcgi_params
        include fastcgi_params;
        # fastcgi settings
        ...
    }

為特定的伺服器啟用PHP支持, 只需要簡單的加入 `include php.conf`就好了:

    /etc/nginx/sites-available/example.conf

    server {
        server_name example.com;
        ...

        include /etc/nginx/php_fastcgi.conf;
    }

##### 測試配置

你需要restart `php-fpm.service` 和 `nginx.service` 服務單元來讓配置生效，如果配置之前被改變過的話.

測試FastCGI實現, 在`root`目錄下創建一個新的PHP文件，內容是:

    <?php phpinfo(); ?>

把這個文件用瀏覽器打開，你應該就可以看到現在的PHP配置的信息頁了.

#### CGI 實現

這個實現是CGI程序所需要的.

##### fcgiwrap

安裝 fcgiwrap包. 配置文件是 `/usr/lib/systemd/system/fcgiwrap.socket`. 啟用並且啟動 `fcgiwrap.socket`.

###### 多個工人線程

如果你想生成多個工人線程建議你使用multiwatchAUR, 它會處理崩潰的子進程. 如果你需要使用`spawn-fcgi` 來創建Unix套接字,因為multiwatch無法處理systemd創建的套接字，儘管fcgiwarp它自己在單元文件中直接調用頁沒有任何問題.

覆蓋 `fcgiwrap.service` (和`fcgiwrap.socket` 單元, 如果存在的話), 修改`ExecStart`行到滿足你需要的地方. 下面是使用multiwatchAUR的單元文件. 確保`fcgiwrap.socket` 沒有被start（開始）或enable（啟用）, 因為他會和這些單元衝突:

    /etc/systemd/system/fcgiwrap.service

    [Unit]
    Description=Simple CGI Server
    After=nss-user-lookup.target

    [Service]
    ExecStartPre=/bin/rm -f /run/fcgiwrap.socket
    ExecStart=/usr/bin/spawn-fcgi -u http -g http -s /run/fcgiwrap.sock -n -- /usr/bin/multiwatch -f 10 -- /usr/sbin/fcgiwrap
    ExecStartPost=/usr/bin/chmod 660 /run/fcgiwrap.sock
    PrivateTmp=true
    Restart=on-failure

    [Install]
    WantedBy=multi-user.target

可以自定義 `-f 10` 來改變生成的子進程的數量.

警告：`ExecStartPost`行是需要的，因為當為`spawn-fcgi`使用`-M 660`參數選項時會出現奇怪的情況.這有可能是一個Bug

##### nginx配置

在每個服務了一個CGI程序的`server` 塊內都應該有一個像下面的 `location` 塊:

    location ~ \.cgi$ {
         root           /path/to/server/cgi-bin;
         fastcgi_pass   unix:/run/fcgiwrap.sock;
         include        fastcgi.conf;
    }

    fcgiwrap 的默認套接字文件是 /run/fcgiwrap.sock.

警告：如果使用了 SCRIPT_NAME 和 DOCUMENT_ROOT，fcgiwrap 會丟棄 nginx 中設置的其他 fastcgi_params。你必須使用 SCRIPT_FILENAME 才能通過 Nginx 配置設置其他參數（如 PATH_INFO）。參見 這個 GitHub 問題。

如果你一直出現 `502 - bad Gateway` 錯誤,你應該檢查你的CGI程序是否宣告了下面內容的mime類型。如果是html，他應該是`Content-type: text/html`.

如果你遇到 403 錯誤，請確保 CGI 可執行文件對 `http` 用戶可讀且可執行，並且每個父文件夾對 `http` 用戶可讀。

## 在chroot環境下安裝

本文或本章節的事實準確性存在爭議。

原因： 本節內容來自2013年。自那時起，systemd已被引入，可以更高效且無需太多麻煩地替代此方法。（在 Talk:Nginx 中討論）

在chroot環境裡運行nginx是有一個額外的安全層的。為了最大化安全性，chroot環境應該只包括nginx伺服器需要的文件，並且所有的文件都應該儘可能有嚴格的權限，比如像`/usr/bin`這樣的文件夾應該不可讀和不可寫。

Arch默認有一個`http`用戶和組來運行伺服器，chroot將會在`/srv/http`。

創建這個監獄（指chroot環境）的PERL腳本能在jail.pl gist找到。你可以直接用那個腳本或者跟著這篇文章的指示繼續閱讀下去。它應該用root運行。你需要在它起作用之前取消一行注釋。

### 創建必要的設備

nginx需要 `/dev/null`, `/dev/random`, 和 `/dev/urandom`. 為了在chroot環境中安裝這些設備，需要創建 `/dev/` 目錄並使用mknod添加設備。儘量避免掛載整個`/dev/`目錄，以確保即使chroot被攻破，攻擊者也必須突破chroot才能訪問像`/dev/sda1`這樣的重要設備。

提示：

- 確保 `/srv/http` 掛載時沒有使用nodev選項
- 查看 mknod(1) 和 `ls -l /dev/{null,random,urandom}` 來更好地理解mknod選項。

    # export JAIL=/srv/http
    # mkdir $JAIL/dev
    # mknod -m 0666 $JAIL/dev/null c 1 3
    # mknod -m 0666 $JAIL/dev/random c 1 8
    # mknod -m 0444 $JAIL/dev/urandom c 1 9

### 創建必要目錄

nginx需要大量文件來運轉正常。在把它們拷貝過去之前創建一個文件夾來存儲它們。假設你的nginx文檔目錄在 `/srv/http/www`。

    # mkdir -p $JAIL/etc/nginx/logs
    # mkdir -p $JAIL/usr/{lib,bin}
    # mkdir -p $JAIL/usr/share/nginx
    # mkdir -p $JAIL/var/{log,lib}/nginx
    # mkdir -p $JAIL/www/cgi-bin
    # mkdir -p $JAIL/{run,tmp}
    # cd $JAIL; ln -s usr/lib lib
    # cd $JAIL; ln -s usr/lib lib64
    # cd $JAIL/usr; ln -s lib lib64

然後掛載 `$JAIL/tmp` 和 `$JAIL/run` 作為tmpfs。大小應該限制確保攻擊者無法吃掉所有的內存。

    # mount -t tmpfs none $JAIL/run -o 'noexec,size=1M'
    # mount -t tmpfs none $JAIL/tmp -o 'noexec,size=100M'

為了在重啟後保留掛載，下面的入口應該被添加到`/etc/fstab`:

    /etc/fstab

    tmpfs   /srv/http/run   tmpfs   rw,noexec,relatime,size=1024k   0       0
    tmpfs   /srv/http/tmp   tmpfs   rw,noexec,relatime,size=102400k 0       0

### 填充chroot

首先複製簡單的文件。

    # cp -r /usr/share/nginx/* $JAIL/usr/share/nginx
    # cp -r /usr/share/nginx/html/* $JAIL/www
    # cp /usr/bin/nginx $JAIL/usr/bin/
    # cp -r /var/lib/nginx $JAIL/var/lib/nginx

然後複製必要的庫。使用ldd來列出它們並將它們複製到正確的位置。複製優於硬連結，以確保即使攻擊者獲得文件的寫權限，也無法破壞或修改真實的系統文件。

    $ ldd /usr/bin/nginx

    linux-vdso.so.1 (0x00007fffc41fe000)
    libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f57ec3e8000)
    libcrypt.so.1 => /usr/lib/libcrypt.so.1 (0x00007f57ec1b1000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f57ebead000)
    libm.so.6 => /usr/lib/libm.so.6 (0x00007f57ebbaf000)
    libpcre.so.1 => /usr/lib/libpcre.so.1 (0x00007f57eb94c000)
    libssl.so.1.0.0 => /usr/lib/libssl.so.1.0.0 (0x00007f57eb6e0000)
    libcrypto.so.1.0.0 => /usr/lib/libcrypto.so.1.0.0 (0x00007f57eb2d6000)
    libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f57eb0d2000)
    libz.so.1 => /usr/lib/libz.so.1 (0x00007f57eaebc000)
    libGeoIP.so.1 => /usr/lib/libGeoIP.so.1 (0x00007f57eac8d000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f57eaa77000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007f57ea6ca000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f57ec604000)

對於 `/usr/lib` 中的文件，你可以使用以下命令：

    # cp $(ldd /usr/bin/nginx | grep /usr/lib/ | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib

使用以下命令複製 `ld-linux-x86-64.so`：

    # cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib

注意：不要嘗試複製 `linux-vdso.so`：它不是真實存在的庫，不在 `/usr/lib` 文件夾中。

複製一些複雜但必要的庫和系統文件。

    # cp /usr/lib/libnss_* $JAIL/usr/lib
    # cp -rfvL /etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx} $JAIL/etc

為chroot創建嚴格的用戶/組文件。這樣只有chroot正常工作所需的用戶才會被chroot知道，其他用戶/組不會洩露給攻擊者，即使他們已經獲取了chroot權限。

    $JAIL/etc/group

    http:x:33:
    nobody:x:99:

    $JAIL/etc/passwd

    http:x:33:33:http:/:/bin/false
    nobody:x:99:99:nobody:/:/bin/false

    $JAIL/etc/shadow

    http:x:14871::::::
    nobody:x:14871::::::

    $JAIL/etc/gshadow

    http:::
    nobody:::

    # touch $JAIL/etc/shells
    # touch $JAIL/run/nginx.pid

最後，設置非常嚴格的權限。儘可能多的文件應歸root所有並設置為不可寫。

    # chown -R root:root $JAIL/

    # chown -R http:http $JAIL/www
    # chown -R http:http $JAIL/etc/nginx
    # chown -R http:http $JAIL/var/{log,lib}/nginx
    # chown http:http $JAIL/run/nginx.pid

    # find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod -rw
    # find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod +x
    # find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs chmod -x
    # find $JAIL/usr/bin -type f -print | xargs chmod ug+rx
    # find $JAIL/ -group http -user http -print | xargs chmod o-rwx
    # chmod +rw $JAIL/tmp
    # chmod +rw $JAIL/run

如果你的埠綁定為80（或其他在[1-1023]範圍內的埠），給chroot無需root即可綁定的權限。

    # setcap 'cap_net_bind_service=+ep' $JAIL/usr/bin/nginx

### 修改nginx.service來開啟chroot

覆蓋單元文件 `nginx.service`。升級nginx不會修改你自定義的 .service 文件。

systemd單元必須在開啟chroot裡的nginx之前修改，以http用戶的身份，將pid文件放到chroot裡。

注意：我不確定pid文件是否需要被放到chroot裡.

    /etc/systemd/system/nginx.service

    [Unit]
    Description=A high performance web server and a reverse proxy server
    After=network.target

    [Service]
    Type=forking
    PIDFile=/srv/http/run/nginx.pid
    ExecStartPre=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -t -q -g 'pid /run/nginx.pid; daemon on; master_process on;'
    ExecStart=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;'
    ExecReload=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;' -s reload
    ExecStop=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid;' -s quit

    [Install]
    WantedBy=multi-user.target

注意：用pacman升級nginx並不會升級chroot裡的nginx。如果你要升級的話可能需要手動重複上面的步驟。不要忘記更新它連結的庫.

你現在可以安全的寫在非chroot環境下的nginx安裝.

    # pacman -Rsc nginx

如果你不移除非chroot環境的nginx安裝，你可能想要確認運行的nginx是不是chroot環境裡的, 你可以查詢 `/proc/PID/root` 連結到哪裡. 如果連結到 `/srv/http` 而不是`/`那運行的就是chroot環境下的nginx.

    # ps -C nginx | awk '{print $1}' | sed 1d | while read -r PID; do ls -l /proc/$PID/root; done

## 建議和技巧

### 使用 systemd 以非特權用戶身份運行

使用 drop-in unit file 為 `nginx.service` 設置 `User` 和可選的 `Group` 選項，放在 `[Service]` 下：

    /etc/systemd/system/nginx.service.d/user.conf

    [Service]
    User=user
    Group=group

我們可以通過以下方式加固服務，防止其提升權限：

    /etc/systemd/system/nginx.service.d/user.conf

    [Service]
    ...
    NoNewPrivileges=yes

提示：更多限制選項請參閱 systemd.exec(5)。

然後我們需要確保 `user` 擁有訪問所需資源的權限。按照以下子章節進行配置，然後啟動 nginx。

提示：相同的設置可能也適用於你的 FastCGI 伺服器。

#### 埠

默認情況下，Linux 不允許非 `root` 進程綁定到 1024 以下的埠。可以使用 1024 以上的埠：

    /etc/nginx/nginx.conf

    server {
            listen 8080;
    }

提示：如果你希望 nginx 在埠 80 或 443 上可訪問，可以配置你的 防火牆 將請求從 80 或 443 重定向到 nginx 監聽的埠。

或者你可以授予 nginx 進程 CAP_NET_BIND_SERVICE 能力，使其能夠綁定到 1024 以下的埠：

    /etc/systemd/system/nginx.service.d/user.conf

    [Service]
    ...
    CapabilityBoundingSet=
    CapabilityBoundingSet=CAP_NET_BIND_SERVICE
    AmbientCapabilities=
    AmbientCapabilities=CAP_NET_BIND_SERVICE

另外，你可以使用 systemd 套接字激活。在這種情況下，systemd 將監聽埠，並在建立連接時啟動 nginx，並將套接字作為文件描述符傳遞。這意味著 nginx 不需要特殊權限，因為套接字在啟動時已經存在。這依賴於 nginx 用於傳遞套接字的內部環境變量 [3]，因此不受官方支持。與其設置 `CapabilityBoundingSet` 和 `AmbientCapabilities`，不如編輯服務覆蓋文件，設置 `NGINX` 環境變量，告訴 nginx 套接字將作為哪些文件描述符傳遞：

    /etc/systemd/system/nginx.service.d/user.conf

    [Service]
    ...
    Environment=NGINX=3:4;

每個監聽埠將有一個套接字，從文件描述符 3 開始，因此在此示例中，我們告訴 nginx 期望兩個套接字。現在創建一個 `nginx.socket` 單元，指定要監聽的埠：

    /etc/systemd/system/nginx.socket

    [Socket]
    ListenStream=0.0.0.0:80
    ListenStream=0.0.0.0:443
    After=network.target
    Requires=network.target

    [Install]
    WantedBy=sockets.target

套接字將按照此單元中定義的順序傳遞，因此埠 80 將是文件描述符 3，埠 443 將是文件描述符 4。如果你之前啟用或啟動了服務，現在應該停止它，並啟用 `nginx.socket`。當系統啟動時，nginx 將不會運行，但在你通過瀏覽器訪問網站時啟動。通過這種方式，你可以進一步加固服務；例如，在許多情況下，你現在可以在服務文件中設置 `PrivateNetwork=True`，阻止 nginx 訪問外部網絡，因為 systemd 創建的套接字足以通過它提供網站服務。請注意，這將在 nginx 服務的日誌中列印一條警告：`2020/08/29 19:33:20 [notice] 254#254: using inherited sockets from "3:4;"`

#### PID 文件

nginx包 默認編譯為使用 `/run/nginx.pid`，而 user 無法寫入該文件。我們可以創建一個 user 可以寫入的目錄，並將 PID 文件放在那裡。例如，可以使用 `RuntimeDirectory` (systemd.exec(5))。

編輯 `nginx.service` 以配置 PID 文件：

    /etc/systemd/system/nginx.service.d/user.conf

    [Service]
    ...
    RuntimeDirectory=nginx
    PIDFile=/run/nginx/nginx.pid
    ExecStart=
    ExecStart=/usr/bin/nginx -g 'pid /run/nginx/nginx.pid; error_log stderr;'
    ExecReload=
    ExecReload=/usr/bin/nginx -s reload -g 'pid /run/nginx/nginx.pid; error_log stderr;'

#### /var/lib/nginx

nginx包 默認編譯為將臨時文件存儲在 `/var/lib/nginx` 中。

提示：通過運行 `$ nginx -V` 查看所有編譯時選項。

你可以通過例如使用 `StateDirectory` (systemd.exec(5)) 來授予 user 對該目錄的寫權限：

    /etc/systemd/system/nginx.service.d/user.conf

    [Service]
    ...
    StateDirectory=nginx

#### /var/log/nginx

nginx包 默認編譯為將訪問日誌存儲在 `/var/log/nginx` 中。

你可以通過例如使用 `LogsDirectory` (systemd.exec(5)) 來授予 user 對該目錄的寫權限：

    /etc/systemd/system/nginx.service.d/user.conf

    [Service]
    ...
    LogsDirectory=nginx

### 使用 systemd 運行用戶服務

如果你想運行一個完全由非特權用戶控制和配置的伺服器實例，請考慮使用 nginx-user-serviceAUR。

### 針對systemd的可選腳本

在純systemd的情況下，你可以享受chroot + systemd的好處。 [4] 基於設置用戶組和 pid：

    /etc/nginx/nginx.conf

    user http;
    pid /run/nginx.pid;

文件的絕對路徑是`/srv/http/etc/nginx/nginx.conf`。

    /etc/systemd/system/nginx.service

    [Unit]
    Description=nginx (Chroot)
    After=network.target

    [Service]
    Type=forking
    PIDFile=/srv/http/run/nginx.pid
    RootDirectory=/srv/http
    ExecStartPre=/usr/bin/nginx -t -c /etc/nginx/nginx.conf
    ExecStart=/usr/bin/nginx -c /etc/nginx/nginx.conf
    ExecReload=/usr/bin/nginx -c /etc/nginx/nginx.conf -s reload
    ExecStop=/usr/bin/nginx -c /etc/nginx/nginx.conf -s stop

    [Install]
    WantedBy=multi-user.target

沒必要設置默認路徑，nginx默認加載`-c /etc/nginx/nginx.conf`，但這仍然是一個好主意。

作為可選項，你可以在chroot環境下僅僅運行 `ExecStart`，`RootDirectoryStartOnly`參數要設置為`yes`（參見 systemd.service(5)），或者在掛載點生效前啟用它，或者使用一個 systemd路徑。

    /etc/systemd/system/nginx.path

    [Unit]
    Description=nginx (Chroot) path
    [Path]
    PathExists=/srv/http/site/Public_html
    [Install]
    WantedBy=default.target

Enable（啟用）創建的`nginx.path` 然後把 `/etc/systemd/system/nginx.service`裡的`WantedBy=default.target` 變成 `WantedBy=nginx.path`。

單元文件裡的`PID文件`允許systemd監控進程（需要絕對路徑）。如果這不是你想要的，你可以改變默認的「一擊」類型，然後刪除單元文件裡的參照。

### Nginx Beautifier （nginx美化器）

nginxbeautifierAUR 是一個命令行工具，用來美化和格式化nginx配置文件。

### 更好的頭文件管理

Nginx 有一個非常不直觀的頭文件管理系統，頭文件只能在一個上下文中定義，任何其它的頭文件都會被忽略。為了補救這個問題，我們可以安裝 headers-more-nginx 模塊。

安裝 nginx-mod-headers-more包 包。這樣會把模塊安裝到 `/usr/lib/nginx/modules` 目錄下。

如果要加載模塊，把下面這行加入到你的 nginx 配置文件的第一行。

    /etc/nginx/nginx.conf

    load_module "/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so";
    ...

### 基本認證

基本認證需要創建一個密碼文件。密碼文件可以使用 apache包 包提供的 `htpasswd` 程序進行管理，或者使用 nginx_passwdAUR 提供的 `nginx-passwd` 進行管理 - 詳細信息可在 GitHub 源碼上找到。

### 使用 php-legacy

安裝 php-legacy-fpm包 而不是 php-fpm包，並確保 PHP 已正確安裝和配置。

PHP-LEGACY-FPM 的主配置文件是 `/etc/php-legacy/php-fpm.conf`。對於基本使用，默認配置應該足夠。

`fastcgi_pass` 參數的 Unix 套接字也需要調整，通常為：

    fastcgi_pass unix:/run/php-fpm-legacy/php-fpm.sock;

然後啟動/啟用 `php-legacy-fpm.service`。

## 故障排除

### 配置驗證

    # nginx -t

     nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
     nginx: configuration file /etc/nginx/nginx.conf test is successful

### Error: The page you are looking for is temporarily unavailable. Please try again later. (502 Bad Gateway)

這是因為FastCGI伺服器還沒有開啟，或者套接字有錯誤的權限。

試一試這個回答來解決502問題。

在Arch Linux，上面提到的配置文件在 `/etc/php/php-fpm.conf`。

### Error: No input file specified

1. 確定`/etc/php/php.ini`裡的 `open_basedir`變量包含正確的路徑，要和 `nginx.conf` (通常在`/usr/share/nginx/`)裡的`root`變量一致. 當使用PHP-FPM 作為PHP的FastCGI伺服器時, 你可以添加 `fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";` 到`nginx.conf`裡的 `location` 代碼塊裡，它能處理PHP文件.

2. 另一種情況是, `nginx.conf`文件裡，`location ~ \.php$`代碼塊裡的錯誤的 `root` 變量. 確保`root` 指向相同伺服器裡的`location /`的同樣的目錄. 或者你直接把root設為全局，不要把它定義到任何地方

3. 檢查權限: 比如. `http` 是用戶/組, `755` 是目錄權限，`644` 是文件權限. 記住，到`html`目錄的路徑目錄也必須有相同的權限. 可以查看 File permissions and attributes#Bulk chmod 來批量修改大量文件夾的權限.

4. 你沒有讓 `SCRIPT_FILENAME` 包含你腳本的全部路徑. 如果nginx的配置 (`fastcgi_param SCRIPT_FILENAME`) 是對的的話, 這種錯誤意味著PHP無法加載請求的腳本. 通常它知識簡單的權限問題, 你可以用root運行php-cgi:

    # spawn-fcgi -a 127.0.0.1 -p 9000 -f /usr/bin/php-cgi

或者你應該創建一個組和用戶來開啟php-cgi:

    # groupadd www
    # useradd -g www www
    # chmod +w /srv/www/nginx/html
    # chown -R www:www /srv/www/nginx/html
    # spawn-fcgi -a 127.0.0.1 -p 9000 -u www -g www -f /usr/bin/php-cgi

5. 如果你是在chroot的環境下的nginx運行php-fpm，確保`/etc/php-fpm/php-fpm.d/www.conf`裡的`chroot` 設置正確(或者 `/etc/php-fpm/php-fpm.conf` 如果是舊版本的話)

### Warning: Could not build optimal types_hash

當開啟 `nginx.service`, 進程可能會有下面的日誌信息:

    [warn] 18872#18872: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size

解決這個警告, 提升`http`代碼塊的下面的鍵的值 [5] [6]:

    /etc/nginx/nginx.conf

    http {
        types_hash_max_size 4096;
        server_names_hash_bucket_size 128;
        ...
    }

### 不能指定請求地址

來自`nginx.service` 單元狀態的全部錯誤是

    [emerg] 460#460: bind() to A.B.C.D:443 failed (99: Cannot assign requested address)

儘管，你的 nginx 單元文件配置為在 `network.target` 之後運行，nginx 可能仍然會嘗試監聽一個已配置但尚未添加到任何接口的地址。通過手動 start （啟動）nginx 來驗證這種情況（從而確保 IP 地址配置正確）。配置 nginx 監聽任何地址都會解決這個問題。如果你的使用場景需要監聽特定地址，一個可能的解決方案是重新配置 systemd。

在所有配置的網絡設備啟動並分配 IP 地址後啟動 nginx，在 `nginx.service` 中將 `network-online.target` 附加到 `After=`，然後 start/enable `systemd-networkd-wait-online.service`。

## 參見

- WebDAV with nginx
- nginx configuration pitfalls
- Very good in-depth 2014 look at nginx security and Reverse Proxying
- Installing LEMP (nginx, PHP, MySQL with MariaDB engine and PhpMyAdmin) in Arch Linux
- Using SSL certificates generated with Let's Encrypt

分類：​

- 網絡伺服器
- 使用「模板:風格」標記的頁面或章節

隱藏分類：​

- 使用「模板:準確性」標記的頁面或章節

Nginx
新增話題