Apache HTTP 服务器

出自 ArchWiki

Apache HTTP 服务器,或简称 Apache,是由 Apache 软件基金会开发的非常流行的 Web 服务器

本文介绍如何设置 Apache,以及如何选择性地将其与 PHP 集成。

安装

安装 apache 软件包。

配置

Apache 配置文件位于 /etc/httpd/conf 目录中。主配置文件是 /etc/httpd/conf/httpd.conf,它包含了各种其他配置文件。默认配置文件对于简单设置来说应该足够好。默认情况下,它会将 /srv/http 目录提供给任何访问您网站的人。

要运行 Apache,启动 httpd.service。如果一切正常,访问 https://127.0.0.1/ 应该会显示一个简单的索引页面。

有关可选的进一步配置,请参阅以下章节。

高级选项

请参阅 Apache 配置指令的完整列表指令快速参考

/etc/httpd/conf/httpd.conf 中的这些选项可能对您有用

User http
出于安全原因,一旦 Apache 由 root 用户(直接或通过启动脚本)启动,它就会切换到此 UID。默认用户是 http,它在安装过程中自动创建。
Listen 80
这是 Apache 将监听的端口。对于通过路由器进行互联网访问,您必须转发端口。
如果您想为本地开发设置 Apache,您可能希望它只能从您的计算机访问。然后将此行更改为 Listen 127.0.0.1:80
ServerAdmin you@example.com
这是管理员的电子邮件地址,可以在错误页面等地方找到。
DocumentRoot "/srv/http"
这是您应该放置网页的目录。
如果您想更改它,请更改它,但不要忘记将 <Directory "/srv/http"> 也更改为您更改 DocumentRoot 的任何内容,否则当您尝试访问新的文档根目录时,可能会收到 403 错误(缺少权限)。不要忘记将 Require all denied 行更改为 Require all granted,否则您将收到 403 错误。请记住,DocumentRoot 目录及其父文件夹必须允许其他人执行权限(可以使用 chmod o+x /path/to/DocumentRoot 设置),否则您将收到 403 错误
AllowOverride None
<Directory> 部分中的此指令导致 Apache 完全忽略 .htaccess 文件。请注意,这现在是 Apache 2.4 的默认设置,因此如果您计划使用 .htaccess 文件,则需要显式允许覆盖。如果您打算在 .htaccess 文件中使用 mod_rewrite 或其他设置,您可以允许该文件中声明的哪些指令可以覆盖服务器配置。有关更多信息,请参阅 Apache 文档
提示: 如果您在配置方面遇到问题,可以使用以下命令让 Apache 检查配置:apachectl configtest

更多设置可以在 /etc/httpd/conf/extra/httpd-default.conf 中找到

关闭服务器签名

ServerSignature Off

隐藏服务器信息,如 Apache 和 PHP 版本

ServerTokens Prod

用户目录

用户目录默认通过 https://127.0.0.1/~yourusername/ 可用,并显示 ~/public_html 的内容(这可以在 /etc/httpd/conf/extra/httpd-userdir.conf 中更改)。

如果您不希望用户目录在 Web 上可用,请注释掉 /etc/httpd/conf/httpd.conf 中的以下行

Include conf/extra/httpd-userdir.conf

此文章或章节的事实准确性存在争议。

理由: 没有必要为每个用户设置 +x,仅通过 ACL 为 Web 服务器设置它就足够了(请参阅访问控制列表#私有文件的执行权限)。(在 Talk:Apache HTTP Server#用户目录 中讨论)

您必须确保您的主目录权限设置正确,以便 Apache 可以访问那里。您的主目录和 ~/public_html 必须对其他人(“世界其他地方”)可执行

$ chmod o+x ~
$ chmod o+x ~/public_html
$ chmod -R o+r ~/public_html

重启 httpd.service 以应用任何更改。另请参阅 Umask#设置掩码值

TLS

警告: 如果您部署 TLS,请务必遵循 weakdh.org 的指南 以防止漏洞。有关更多信息,请参阅 服务器端 TLS

首先获取证书。如果您拥有公共域名,则可以使用 传输层安全性#ACME 客户端

/etc/httpd/conf/httpd.conf 中,取消注释以下三行

LoadModule ssl_module modules/mod_ssl.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
Include conf/extra/httpd-ssl.conf

如果使用 Certbot (certbot --apache),则还需要取消注释以下行

LoadModule rewrite_module modules/mod_rewrite.so

获取密钥和证书后,请确保 /etc/httpd/conf/extra/httpd-ssl.conf 中的 SSLCertificateFileSSLCertificateKeyFile 行指向密钥和证书。如果还生成了 CA 证书的串联链,请将该文件名添加到 SSLCertificateChainFile

最后,重启 httpd.service 以应用任何更改。

提示: Mozilla 提供了一篇有用的 SSL/TLS 文章 以及一个 自动化工具,以帮助创建更安全的配置。

虚拟主机

注意: 您需要为虚拟主机 SSL 支持添加单独的 <VirtualHost *:443> 部分。有关示例文件,请参阅 #管理多个虚拟主机

如果您想拥有多个主机,请取消注释 /etc/httpd/conf/httpd.conf 中的以下行

Include conf/extra/httpd-vhosts.conf

/etc/httpd/conf/extra/httpd-vhosts.conf 中设置您的虚拟主机。默认文件包含一个详细的示例,应该可以帮助您入门。

要在本地计算机上测试虚拟主机,请将虚拟名称添加到您的 /etc/hosts 文件中

127.0.0.1 domainname1.dom 
127.0.0.1 domainname2.dom

重启 httpd.service 以应用任何更改。

管理多个虚拟主机

注意: 如果您计划使用 certbot 部署 TLS 证书,则必须将您的虚拟服务器分隔到单独的文件中。将所有虚拟服务器列在单个文件中(就像默认情况一样)会导致 certbot 无法正确部署证书

如果您有大量的虚拟主机,您可能希望轻松地禁用和启用它们。建议为每个虚拟主机创建一个配置文件,并将它们都存储在一个文件夹中,例如:/etc/httpd/conf/vhosts

首先创建文件夹

# mkdir /etc/httpd/conf/vhosts

然后将单个配置文件放在其中

# nano /etc/httpd/conf/vhosts/domainname1.dom
# nano /etc/httpd/conf/vhosts/domainname2.dom
...

在最后一步中,在您的 /etc/httpd/conf/httpd.confInclude 单个配置

#Enabled Vhosts:
Include conf/vhosts/domainname1.dom
Include conf/vhosts/domainname2.dom

您可以通过注释或取消注释单个虚拟主机来启用和禁用它们。

一个非常基本的 vhost 文件将如下所示

/etc/httpd/conf/vhosts/domainname1.dom
<VirtualHost *:80>
    ServerAdmin webmaster@domainname1.dom
    DocumentRoot "/home/user/http/domainname1.dom"
    ServerName domainname1.dom
    ServerAlias domainname1.dom
    ErrorLog "/var/log/httpd/domainname1.dom-error_log"
    CustomLog "/var/log/httpd/domainname1.dom-access_log" common

    <Directory "/home/user/http/domainname1.dom">
        Require all granted
    </Directory>
</VirtualHost>

<VirtualHost *:443>
    ServerAdmin webmaster@domainname1.dom
    DocumentRoot "/home/user/http/domainname1.dom"
    ServerName domainname1.dom:443
    ServerAlias domainname1.dom:443
    SSLEngine on
    SSLCertificateFile "/etc/httpd/conf/server.crt"
    SSLCertificateKeyFile "/etc/httpd/conf/server.key"
    ErrorLog "/var/log/httpd/domainname1.dom-error_log"
    CustomLog "/var/log/httpd/domainname1.dom-access_log" common

    <Directory "/home/user/http/domainname1.dom">
        Require all granted
    </Directory>
</VirtualHost>

扩展

PHP

首先安装 PHP,然后按照下面接下来的三个子章节之一进行操作。最后,按照最后一个子章节中的描述测试安装。

使用 libphp

此方法可能是最简单的,但也是可伸缩性最差的:它适用于轻请求负载。它还要求您更改 mpm 模块,这可能会导致其他扩展出现问题(例如,它与 #HTTP/2 不兼容)。

安装 php-apache

注意: 如果您使用的是来自 AUR 的固定的旧版本 PHP,那么您应该安装相应固定的 php-apache 版本,即 php56-apacheAUR 用于 php56AUR

/etc/httpd/conf/httpd.conf 中,注释掉该行

#LoadModule mpm_event_module modules/mod_mpm_event.so

并取消注释该行

LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
注意: 上面是必需的,因为软件包中包含的 libphp.so 不适用于 mod_mpm_event,而仅适用于 mod_mpm_prefork。(FS#39218)

否则您将收到以下错误

Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe.  You need to recompile PHP.
AH00013: Pre-configuration failed
httpd.service: control process exited, code=exited status=1
作为替代方案,您可以使用 mod_proxy_fcgi(请参阅下面的 #使用 php-fpm 和 mod_proxy_fcgi)。

要启用 PHP,请将以下行添加到 /etc/httpd/conf/httpd.conf

  • 将其放在 LoadModule 列表的末尾
LoadModule php_module modules/libphp.so
AddHandler php-script .php
  • 将其放在 Include 列表的末尾
Include conf/extra/php_module.conf

然后重启 httpd.service

注意: 对于 AUR 固定的 PHP 版本,请相应地替换上面代码中的 php_modulelibphp.sophp_module.conf。 例如,对于 php56-apacheAUR,应替换为 php5_modulelibphp56.sophp56-module.conf


如果您不确定正确的替换方式,请使用 pacman -Ql phpXX-apache 列出 libphp.sophp_module.conf 的正确替换,然后使用 nm /usr/lib/httpd/modules/libphpXX.so 查找 php_module 的正确替换。

使用 apache2-mpm-worker 和 mod_fcgid

此方法在处理多个请求时提供更好的性能和内存使用率。

安装 mod_fcgidAURphp-cgi

创建所需的目录并为其 PHP 包装器创建符号链接

# mkdir /srv/http/fcgid-bin
# ln -s /usr/bin/php-cgi /srv/http/fcgid-bin/php-fcgid-wrapper

创建 /etc/httpd/conf/extra/php-fcgid.conf,内容如下

/etc/httpd/conf/extra/php-fcgid.conf
# Required modules: fcgid_module

<IfModule fcgid_module>
    AddHandler php-fcgid .php
    AddType application/x-httpd-php .php
    Action php-fcgid /fcgid-bin/php-fcgid-wrapper
    ScriptAlias /fcgid-bin/ /srv/http/fcgid-bin/
    SocketPath /var/run/httpd/fcgidsock
    SharememPath /var/run/httpd/fcgid_shm
        # If you don't allow bigger requests many applications may fail (such as WordPress login)
        FcgidMaxRequestLen 536870912
        # Path to php.ini – defaults to /etc/phpX/cgi
        DefaultInitEnv PHPRC=/etc/php/
        # Number of PHP childs that will be launched. Leave undefined to let PHP decide.
        #DefaultInitEnv PHP_FCGI_CHILDREN 3
        # Maximum requests before a process is stopped and a new one is launched
        #DefaultInitEnv PHP_FCGI_MAX_REQUESTS 5000
    <Location /fcgid-bin/>
        SetHandler fcgid-script
        Options +ExecCGI
    </Location>
</IfModule>

编辑 /etc/httpd/conf/httpd.conf

  • 取消注释 actions 模块的加载行
    LoadModule actions_module modules/mod_actions.so
  • 在加载 unixd 模块之后加载 FCGID 模块(它依赖于 unixd 模块) - 您可能希望将其放置在 <IfModule unixd_module> 代码块中
    LoadModule fcgid_module modules/mod_fcgid.so
  • 确保 MPM 配置的包含行已取消注释(在默认安装的此文件中,它是取消注释的)
    Include conf/extra/httpd-mpm.conf
  • 添加新 FCGID 配置的包含行
    Include conf/extra/php-fcgid.conf

重启 httpd.service

使用 php-fpm 和 mod_proxy_fcgi

此方法提供了“另一种 PHP FastCGI 实现,它具有一些额外的功能,(主要)对高负载站点有用” [1]

注意: 与广泛使用的 ProxyPass 设置不同,使用 SetHandler 的代理配置会遵循其他 Apache 指令,例如 DirectoryIndex。 这确保了与为 libphp、mod_fastcgi 和 mod_fcgid 设计的软件更好的兼容性。 如果您仍然想尝试 ProxyPass,请尝试类似这样的行
ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/run/php-fpm/php-fpm.sock|fcgi://127.0.0.1/srv/http/$1

安装 php-fpm

启用 proxy 模块

/etc/httpd/conf/httpd.conf
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

创建 /etc/httpd/conf/extra/php-fpm.conf,内容如下

DirectoryIndex index.php index.html
<FilesMatch \.php$>
    SetHandler "proxy:unix:/run/php-fpm/php-fpm.sock|fcgi://127.0.0.1/"
</FilesMatch>

并将其包含在 /etc/httpd/conf/httpd.conf 的底部

Include conf/extra/php-fpm.conf
注意: sockfcgi 之间的管道符不允许被空格包围! localhost 可以替换为任何字符串。 更多信息请参考 这里

您可以在 /etc/php/php-fpm.d/www.conf 中配置 PHP-FPM,但默认设置应该可以正常工作。

启动启用 php-fpm.service,然后 重启 httpd.service

测试 PHP 是否工作

要测试 PHP 是否配置正确,请在您的 Apache DocumentRoot 目录(例如 /srv/http/~<username>/public_html/)中创建一个名为 test.php 的文件,内容如下

<?php phpinfo(); ?>

然后访问 https://127.0.0.1/test.php 或 https://127.0.0.1/~<username>/test.php,视情况而定。

HTTP/2

注意
  • 虽然 Apache 支持通过 TCP 的未加密 HTTP/2 (h2c),但常见的浏览器不支持。 因此,为了与后者一起使用,必须首先启用 #TLS
  • 如果支持的客户端没有使用 HTTP/2 而不是 HTTP/1.1,并且 Mozilla 的配置生成器(已经包含下面的 Protocols 行)被用于设置 #TLS,请尝试在后者的输出之后 Include 包含 httpd-ssl.conf
  • 测试方法包括 curl -sI https://your.website 或使用 http indicator(支持基于 Chromium 的浏览器和基于 Firefox 的浏览器)。

要启用通过 TLS 的 HTTP/2 支持,请取消注释 httpd.conf 中的以下行

LoadModule http2_module modules/mod_http2.so

并添加以下行

Protocols h2 http/1.1

要进行调试,您可以仅将模块而不是整个服务器设置为 debuginfo

<IfModule http2_module>
    LogLevel http2:info
</IfModule>

有关更多信息(包括额外的 HTTP/2 功能设置),请参阅 mod_http2 文档。

警告: http2_module 与旧配置广泛用于设置 PHP 的 mpm_prefork_module 不兼容。 考虑使用 php-fpm 代替。

故障排除

Apache 状态和日志

使用 systemctl 查看 Apache 守护进程的状态。

Apache 日志可以在 /var/log/httpd/ 中找到

错误:启动后 PID 文件 /run/httpd/httpd.pid 不可读(尚未?)

注释掉 httpd.conf 中的 unique_id_module 行: #LoadModule unique_id_module modules/mod_unique_id.so

/run/httpd 未在启动时创建

如果 root 用户执行 systemd-tmpfiles --create 时抱怨“unsafe path transition”,请检查您的根目录的所有权。

ls -la /
chown root:root /

Apache 正在运行线程化的 MPM,但您的 PHP 模块未编译为线程安全。

如果在加载 php_modulehttpd.service 失败,您可能会在日志中收到类似这样的错误

Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe.  You need to recompile PHP.

这是因为 PHP 包含对非线程安全模块的支持,并且您正在尝试使用线程化的 MPM。 解决此问题的一种方法是使用非线程化的 MPM。 尝试将 mpm_event_module 替换为 mpm_prefork_module

/etc/httpd/conf/httpd.conf
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

重启 httpd.service

警告: 某些其他模块(如 http2_module)在 mpm_prefork 激活时会禁用自身。

AH00534: httpd: Configuration error: No MPM loaded.

您可能在最近的升级后遇到此错误。 这只是 httpd.conf 中最近更改的结果,您可能尚未在本地配置中重现该更改。 要修复它,请取消注释以下行

/etc/httpd/conf/httpd.conf
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

重启 httpd.service

AH00072: make_sock: could not bind to address

这可能是由多种原因引起的。 最常见的问题是某些东西已经在给定的端口上监听,通过 ss 检查是否发生这种情况

# ss -lnp | grep -e :80 -e :443

如果您得到任何输出,请停止占用端口的给定服务或杀死导致端口绑定的失控进程,然后重试。

另一个问题可能是 Apache 没有以 root 身份启动 - 尝试手动启动它,看看是否仍然收到 AH0072 错误。

# httpd -k start

最后,您的配置也可能存在错误,并且您在给定端口上监听了两次。 以下是一个错误配置的示例,它将触发此问题

Listen 0.0.0.0:80
Listen [::]:80

AH01071: Got error 'Primary script unknown'

如果您在虚拟主机环境中为 /home 中的文件提供服务,则可能是 php-fpm systemd 单元文件中 ProtectHome=true 引起的。 您可以通过编辑 php-fpm 单元文件重启 php-fpm.service 来禁用此功能。 或者,移动您的文档根目录。

更改 php.ini 中的 max_execution_time 无效

如果您在 php.ini 中将 max_execution_time 更改为大于 30(秒)的值,您仍然可能会在 30 秒后从 Apache 收到 503 Service Unavailable 响应。 要解决此问题,请在 <FilesMatch \.php$> 代码块之前在您的 http 配置中添加 ProxyTimeout 指令

/etc/httpd/conf/httpd.conf
ProxyTimeout 300

重启 httpd.service

PHP-FPM:错误没有按虚拟主机单独记录

如果您有多个虚拟主机,则可能希望将每个虚拟主机的错误日志输出到单独的文件(使用 ErrorLog Apache 指令)。 如果这对您不起作用,请确认 PHP-FPM 配置为将错误记录到 syslog

/etc/php/php-fpm.conf
error_log = syslog

池配置也可能覆盖它。 确保以下行被注释掉

/etc/php/php-fpm.d/www.conf
;php_admin_value[error_log] = /var/log/fpm-php.www.log

另请参阅