文章目录
建议先了解一下上一篇文章 Apache HTTP配置虚拟主机(一)
1. 使用CGI提供动态内容
CGI(通用网关接口)为Web服务器定义了一种与外部内容生成程序进行交互的方式,这些程序通常称为CGI程序或CGI脚本。这是在网站上放置动态内容的最简单,最常见的方法。
1.1 配置Apache启用CGI
如果Apache是使用共享模块支持构建的,则需要确保已加载该模块。在httpd.conf中,需要确保该 LoadModule 指令未被注释掉:
LoadModule cgid_module modules/mod_cgid.so
在Windows上,或使用非线程MPM(例如prefork),正确配置的指令如下所示:
LoadModule cgi_module modules/mod_cgi.so
因为我使用系统默认的配置,所以httpd使用prefork,使用 cgi_module 模块,没有使用cgid_module模块。
# httpd -V
Server version: Apache/2.2.15 (Unix)
...
Server MPM: Prefork
# httpd -t -D DUMP_MODULES
...
cgi_module (shared)
1.1.1 ScriptAlias
ScriptAlias将URL映射到文件系统位置,并将目标指定为CGI脚本。句法为:
ScriptAlias URL-path file-path|directory-path
句法中的URL-path将被映射到以第二个参数开头的脚本,该参数是本地文件系统中的完整路径名。
示例:
ScriptAlias /cgi-bin/ /web/cgi-bin/
注意:/web/cgi-bin/最后的斜杠不能少!否则无法定位到该目录下的程序!
一个指向 http://example.com/cgi-bin/foo 的请求将会使服务器运行脚本 /web/cgi-bin/foo。此配置实质上等效于:
Alias /cgi-bin/ /web/cgi-bin/
<Location /cgi-bin >
SetHandler cgi-script
Options +ExecCGI
</Location>
Alias 和 ScriptAlias 通常用于 DocumentRoot 目录之外的目录,它们之间的区别就是 ScriptAlias 有附加含义,即该URL前缀下的所有内容都将被视为CGI程序。
最好避免将CGI脚本放置在 DocumentRoot(即网站根目录),以免在配置发生更改时意外泄露源代码。如果确实需要将CGI脚本放在可从Web访问的目录中,请不要使用 ScriptAlias,而是使用<Directory>,SetHandler和,Options,如:
<Directory /usr/local/apache2/htdocs/cgi-bin >
SetHandler cgi-script
Options ExecCGI
</Directory>
这是必要的,因为多个URL路径可以映射到相同的文件系统位置,如果它们不受<Directory>的限制,则有可能绕过 ScriptAlias 并暴露CGI脚本的源代码。
1.1.2 ScriptAlias目录之外的CGI
出于安全原因,CGI程序通常仅限于ScriptAlias目录。这样,管理员可以严格控制允许谁使用CGI程序。但是,如果采取了适当的安全预防措施,则可以从任意目录运行CGI程序。例如,您可能希望使用UserDir指令让用户在其主目录中拥有Web内容 。如果他们想拥有自己的CGI程序,但无权访问主cgi-bin目录,则他们将需要能够在其他地方运行CGI程序。
允许在任意目录中执行CGI有两个步骤。首先,必须使用AddHandler或SetHandler指令激活cgi脚本。以下AddHandler指令告诉服务器将带有cgi或pl扩展名的所有文件视为CGI程序。
AddHandler cgi-script .cgi .pl
其次, 必须在Options指令中指定ExecCGI。下面的指令告诉Apache允许执行CGI文件。
<Directory /usr/local/apache2/htdocs/somedir>
Options +ExecCGI
</Directory>
示例,允许CGI程序执行用户目录下以.cgi结尾的任何文件 ,可以使用以下配置:
<Directory /home/*/public_html>
Options +ExecCGI
AddHandler cgi-script .cgi
</Directory>
如果希望指定一个用户目录的cgi-bin子目录,其中所有内容都将被视为cgi程序,则可以使用以下命令:
<Directory /home/*/public_html/cgi-bin>
Options ExecCGI
SetHandler cgi-script
</Directory>
1.2 编写CGI程序
``常规’'编程和CGI编程之间有两个主要区别。首先,CGI程序的所有输出内容必须在MIME-type报头之后,这个HTTP类型的报头告诉客户端它正在接收哪种内容。大多数时候,这看起来像:
Content-type: text/html
其次,输出必须为HTML或浏览器将能够显示的其他格式。大多数情况下是HTML,但有时可能会编写一个CGI程序来输出gif图像或其他非HTML内容。
1.2.1 第一个CGI程序
输入以下内容,将其保存到名为的文件中first.pl,并将其放在 cgi-bin目录中。
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello, World.";
第一行告诉apache,需要使用perl来解释这个程序;第二行打印报文的内容类型声明,然后是两个回车换行符对。这将在报头后放置一个空行,以指示HTTP报头的结尾和正文内容的开头;第三行打印内容。
该脚本用浏览器打开如下所示:
如果报错,检查一下,确认赋予程序执行权限,并尝试在命令行运行:
# ./first.pl
Content-type: text/html
Hello, World.
2. 动态配置的大规模虚拟主机
2.1 动态虚拟主机的产生
如果httpd.conf包含许多 <VirtualHost>基本相同的部分,则此处介绍的技术很有用 。例如有3台虚拟主机,其配置文件位于/etc/httpd/conf.d,内容如下:
# cat virtualhost.conf
<VirtualHost 192.168.198.133>
ServerName customer-1.example.com
DocumentRoot "/www/hosts/customer-1.example.com/docs"
ScriptAlias "/cgi-bin/" "/www/hosts/customer-1.example.com/cgi-bin/"
</VirtualHost>
<VirtualHost 192.168.198.133>
ServerName customer-2.example.com
DocumentRoot "/www/hosts/customer-2.example.com/docs"
ScriptAlias "/cgi-bin/" "/www/hosts/customer-2.example.com/cgi-bin/"
</VirtualHost>
<VirtualHost 192.168.198.133>
ServerName customer-3.example.com
DocumentRoot "/www/hosts/customer-3.example.com/docs"
ScriptAlias "/cgi-bin/" "/www/hosts/customer-3.example.com/cgi-bin/"
</VirtualHost>
通过上文,我们知道ScriptAlias将访问响应目录下的CGI程序并返回运行结果。我们把上文的first.pl改动一下,分别放到3台主机相应文件夹:
# cat /www/hosts/customer-1.example.com/cgi-bin/test.pl
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello, customer-1.example.com.";
# cat /www/hosts/customer-2.example.com/cgi-bin/test.pl
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello, customer-2.example.com.";
# cat /www/hosts/customer-3.example.com/cgi-bin/test.pl
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello, customer-3.example.com.";
在每个虚拟主机根目录下新建index.html,重新加载httpd,使用浏览器访问http://customer-1.example.com和http://customer-1.example.com/cgi-bin/test.pl,可以看到环境以及ok了。
如果有大量配置相似的虚拟主机,我们希望<VirtualHost>用一种动态解决这些问题的机制来替换这些多个 模块。这具有许多优点:
- 配置文件会变小,因此Apache启动速度更快,并且使用的内存更少。也许更重要的是,较小的配置更易于维护,并且留出更少的错误空间。
- 添加虚拟主机仅需在文件系统中创建适当的目录,并在DNS中创建相应解析即可,无需重新配置或重新启动Apache。
主要缺点是每个虚拟主机不能有不同的日志文件。不过我们可以用上一节介绍的split-logfile脚本将日志按照虚拟主机进行拆分。
2.2 动态虚拟主机的实现基础
虚拟主机由两部分信息定义:其IP地址和HTTP请求中Host:报头的内容。Host:报头类似:
# curl -v http://www.baidu.com
* About to connect() to www.baidu.com port 80 (#0)
...
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
为了使动态虚拟主机看起来像普通主机,需要从HTTP请求中确定几件事。最重要的是服务器名称,运行时使用的实际值由 UseCanonicalName 设置控制。配置为 UseCanonicalName Off,服务器名称取自Host:报头的内容。用UseCanonicalName DNS,它取自虚拟主机IP地址的反向DNS查找。前一种设置用于基于名称的动态虚拟主机,而后一种设置用于基于IP的主机。如果httpd因为没有Host:报头而无法计算出服务器名称,或者DNS查找失败,那么将ServerName配置为服务器名称。
要确定的另一件事是文档根目录。在正常配置中,核心模块使用DocumentRoot将URI映射到文件名,DocumentRoot通过DOCUMENT_ROOT环境变量使用CGI脚本;在使用动态虚拟主机时,该工作必须由另一个模块(mod_vhost_alias或 mod_rewrite)接管,这两个模块都不负责设置DOCUMENT_ROOT环境变量,因此需要设置动态虚拟主机的根目录。
2.3 使用mod_vhost_alias的动态虚拟主机
本示例使用mod_vhost_alias将2.1节的3台虚拟主机配置为动态虚拟主机。在httpd.conf中进行如下配置:
# get the server name from the Host: header
UseCanonicalName Off
# this log format can be split per-virtual-host based on the first field
# using the split-logfile utility.
LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon
CustomLog "logs/access_log" vcommon
# include the server name in the filenames used to satisfy requests
VirtualDocumentRoot "/www/hosts/%0/docs"
VirtualScriptAlias "/www/hosts/%0/cgi-bin"
变量 %0 表示引用HTTP请求的服务器名,如 Host:报文所示。移除原虚拟主机配置,重新加载httpd,即可验证。
注意:确保注释掉httpd.conf中/cgi-bin/的配置,否则脚本请求会被转发到/var/www/cgi-bin目录下,导致实验失败。
<IfModule alias_module>
...
#ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
</IfModule>
个人感觉动态虚拟主机在实际生产环境中应用比较少,所以就写到这吧。
参考文档
http://httpd.apache.org/docs/2.2/vhosts/