Apache深度优化
- 2019 年 12 月 24 日
- 筆記
一、开启apache的Gzip(deflate)功能 gzip可以极大的加速网站,压缩比率通常在40%~80%之间,在之前的版本中,Gizp是第三方压缩工具,但是在Gzip 2版本后,Apache自己开发了deflate模块,用来实现压缩功能。
如果要开启apache的压缩功能,需要在编译安装apache时,增加“–enable-deflate”配置项,并且必须在主配置文件中打开下面两个模块:
LoadModule deflate_module modules/mod_deflate.so LoadModule headers_module modules/mod_headers.so
注意:如果在编译安装时,没有增加“–enable-deflate”选项,可以使用DSO方式安装此功能,如下:
[root@www ~]# cd /root/httpd-2.4.23/modules/filters/ #切换至apache 源码包 mod_deflate 所在的目录下 [root@www ~]# /usr/local/http-2.4.23/bin/apxs -c -i -a mod_deflate.c #以 dso 的方式编译安装到 apache 中
如果报错如下:

此报错是缺少zlib-devel的安装包,直接执行“yum -y install zlib-devl”进行安装即可,然后再次安装deflate功能。
确认安装成功:
[root@www filters]# ll /usr/local/http-2.4.23/modules/mod_deflate.so #检查 mod_deflate 是否安装,成功安装这里会显示出该文件
关于上面执行的命令“/usr/local/http-2.4.23/bin/apxs -c -i -a mod_deflate.c”。
选项解释如下:
- -c:表示需要执行编译操作;
- -i:表示需要执行安装操作,以安装一个或多个动态共享对象到服务器的modeles目录中。
- -a:此选项是自动增加一个LoadModule 行到 httpd.conf 文件中,以启用此模块,或者,如果此行已经存在,则启用它。
安装完成后,默认是启用了这两个模块的,但是检查apache的主配置文件时,可能会报错,如下:

解决办法:需要在 LoadModule deflate_modulemodules/mod_deflate.so 配置项的前面加载 zlib.so。刚才yum安装zlib时,它默认安装在了/usr/lib64目录下,所以在apache主配置文件中,在 LoadModule deflate_module modules/mod_deflate.so 这行的上一行添加 LoadFile /usr/lib64/libz.so 即可。
最后确认开启压缩功能(apache主配置文件中必须开启以下三行,这三行不一定在同一位置,但是LoadFile /usr/lib64/libz.so必须在那两个之前):
LoadFile /usr/lib64/libz.so LoadModule deflate_module modules/mod_deflate.so LoadModule headers_module modules/mod_headers.so
至此,压缩功能就开启了,接下来配置压缩的功能。
在apache的主配置文件的末尾写入以下内容(在添加代码前最好先确定以下代码是否存在):
<IfModule mod_deflate.c> <!--deflate模块配置--> DeflateCompressionLevel 6 <!--压缩等级范围0-9,数字越大,压缩比越高,建议取中,否则会加大CPU的压力--> SetOutputFilter DEFLATE <!--设置输出过滤器,对输出启用压缩,就像一个开关一样,告诉apache对传输到浏览器的内容进行压缩--> AddOutputFilterByType DEFLATE text/* <!--设置对文件是对文本的内容进行压缩,如text/html text/css text/plain等--> AddOutputFilterByType DEFLATE application/ms* application/vnd* application/postscript application/javascript application/x-javascript <!--对JavaScript文件进行压缩--> AddOutputFilterByType DEFLATE application/x-httpd-php application/x-httpd-fastphp <!--对php类型的文件进行压缩--> SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary <!--设置图片类型不进行压缩,图片压缩后的大小可能超过原来的大小,而且可能会失真,所以不对图片进行压缩。注:?号表示不会捕获()里的内容了--> SetEnvIfNoCase Request_URI .(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary <!--同上,就是设置不对 exe,tgz,gz 等的文件进行压缩--> SetEnvIfNoCase Request_URI .(?:pdf|mov|avi|mp3|mp4|rm)$ no-gzip dont-vary <!--同上就是设置不对 pdf,avi,mp3 等的文件进行压缩--> </IfModule> <!--以下是设置压缩信息的日志输出--> DeflateFilterNote Input input_info <!--声明输入流的 byte 数量--> DeflateFilterNote Output output_info <!--声明输出流的 byte 数量--> DeflateFilterNote Ratio ratio_info <!--声明压缩的百分比--> LogFormat '"%r" %{output_info}n/%{input_info}n (%{ratio_info}n%%)' deflate <!--声明日志格式--> CustomLog logs/deflate_log.log deflate <!--指定日志的存放路径-->
可根据上面的配置,定义自己需要的压缩项目,要压缩的文件类型根据自己所需要的配置即可(注:除了图片之外,flash 的 swf 文件也是不用启用 GZip 压缩的) 去除注释后的代码如下:
<IfModule mod_deflate.c> DeflateCompressionLevel 6 SetOutputFilter DEFLATE AddOutputFilterByType DEFLATE text/* AddOutputFilterByType DEFLATE application/ms* application/vnd* application/postscript application/javascript application/x-javascript AddOutputFilterByType DEFLATE application/x-httpd-php application/x-httpd-fastphp SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary SetEnvIfNoCase Request_URI .(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary SetEnvIfNoCase Request_URI .(?:pdf|mov|avi|mp3|mp4|rm)$ no-gzip dont-vary </IfModule> DeflateFilterNote Input input_info DeflateFilterNote Output output_info DeflateFilterNote Ratio ratio_info LogFormat '"%r" %{output_info}n/%{input_info}n (%{ratio_info}n%%)' deflate CustomLog logs/deflate_log.log deflate
修改完成后,保存退出并且重启http服务,使用谷歌浏览器访问(在访问前按“F12”),即可看到压缩相关的信息(最好修改网页的文件,使其大一些,否则不会启用压缩,看不到效果),如下:

查看压缩信息产生的日志:
[root@apache http-2.4.23]# cat logs/deflate_log.log #查看压缩日志 "GET / HTTP/1.1" 74/4545 (1%) #74表示压缩后的大小,4545表示压缩前的大小,末尾的括号中是压缩率 "GET / HTTP/1.1" 74/4545 (1%) "-" -/- (-%) [root@apache http-2.4.23]# ll htdocs/index.html #可以查看网页的大小是否为压缩前的大小“4545” -rw-r--r--. 1 root root 4545 10月 12 23:00 htdocs/index.html
二、开启expires缓存功能 expires功能可以较少20%~30%左右的重复请求,让重复的用户对指定的页面请求结果都cache在本地,而无需向服务器发出请求。但是经常发生更改的文件不建议这么做。
1、在没有缓存机制的情况下,先查看以下获取的响应报文。
[root@apache htdocs]# curl -I 127.0.0.1/test.jpg #访问一个图片 HTTP/1.1 200 OK Date: Sun, 13 Oct 2019 02:10:34 GMT Server: Apache/2.4.23 (Unix) Last-Modified: Mon, 22 Jul 2019 05:55:51 GMT ETag: "415bf-58e3eba7687c0" Accept-Ranges: bytes Content-Length: 267711 Content-Type: image/jpeg #这就是内容的格式,在定义缓存机制时的类型就是根据这来定义的 #可以看到,上面没有出现cache等相关字样
2、配置expires缓存功能:
[root@apache htdocs]# vim /usr/local/http-2.4.23/conf/httpd.conf #编辑主配置文件 ..................#省略部分内容 LoadModule expires_module modules/mod_expires.so #去掉此行注释符号 #然后在配置文件的末尾添加以下expires规则 <IfModule mod_expires.c> ExpiresActive On #开启缓存机制 #以下是定义各种类型的文件缓存多长时间 ExpiresByType text/css "now plus 1 month" ExpiresByType application/x-javascript "now plus 5 day" ExpiresByType image/jpeg "access plus 30 days" #这里将图片的缓存时间设置为30天 ExpiresByType image/gif "access plus 1 month" ExpiresByType image/bmp "access plus 1 month" ExpiresByType image/x-icon "access plus 1 month" ExpiresByType image/png "access plus 1 minute" ExpiresByType application/x-shockwave-flash "access plus 1 month" ExpiresDefault "now plus 0 minute" #这行表示上面没有定义的,则不进行缓存。 </IfModule> #写入后,保存退出即可。 [root@apache htdocs]# apachectl -t #测试配置文件是否有误 Syntax OK [root@apache htdocs]# apachectl restart #重启apache,以便刚才的更改生效
无注释的配置文件如下:
LoadModule expires_module modules/mod_expires.so <IfModule mod_expires.c> ExpiresActive On ExpiresByType text/css "now plus 1 month" ExpiresByType application/x-javascript "now plus 5 day" ExpiresByType image/jpeg "access plus 30 days" ExpiresByType image/gif "access plus 1 month" ExpiresByType image/bmp "access plus 1 month" ExpiresByType image/x-icon "access plus 1 month" ExpiresByType image/png "access plus 1 minute" ExpiresByType application/x-shockwave-flash "access plus 1 month" ExpiresDefault "now plus 0 minute" </IfModule>
缓存机制的配置格式:ExpiresByType type/encoding "<base> [plus] {<num><type>}"
1、其中<base>是下列之一:
- access(相对于客户端访问的时间)
- now(相当于access)
- modification(相对于最后一次修改源文件后的缓存时间)
2、该plus关键字是可选的。num 应该是整数值,并且type是以下之一:
- years
- months
- weeks
- days
- hours
- minutes
- seconds
也可以使用以下格式来定义缓存机制:
ExpiresByType image/jpeg A2592000 #表示图片的缓存是1个月 ExpiresByType text/html M604800 #表示HTML文档的有效期是最后修改时刻后的一星期
如果使用“A”(等同于access)“M”(等同于modification)来定义缓存有效期,那么只能以秒来计算。 结论:expires模块可以将到期日期设置为相对于上次修改源文件的时间,还是相对于客户端访问的时间。 3、进行访问测试,查看是否有缓存机制:
[root@apache htdocs]# curl -I 127.0.0.1/test.jpg #访问某个图片 HTTP/1.1 200 OK Date: Sun, 13 Oct 2019 02:16:44 GMT Server: Apache/2.4.23 (Unix) Last-Modified: Mon, 22 Jul 2019 05:55:51 GMT ETag: "415bf-58e3eba7687c0" Accept-Ranges: bytes Content-Length: 267711 Cache-Control: max-age=2592000 #这就是缓存的时间,单位是秒 Expires: Tue, 12 Nov 2019 02:16:44 GMT Content-Type: image/jpeg
关于缓存的配置格式,可以阅读其官方文档进行详细了解。 三、禁止Apache进行目录遍历 当web服务器收到请求报文时,会自动在网页根目录下寻找index.html文件,那么,如果没有这个文件呢?
[root@apache htdocs]# ls #确认web服务器的网页根目录没有index.html文件 a.sh images test.jpg
客户端就会看到以下界面:

可以看到,如果没有index.html文件,那么我们的网页结构就直接暴露给了client,这样多少会存在一些隐患,所以,怎么解决呢?
解决方法如下:
[root@apache htdocs]# vim ../conf/httpd.conf #编辑主配置文件 Options Indexes FollowSymLinks #定位到这行 #更改如下: Options FollowSymLinks #将中间的Indexes删除即可保存退出 [root@apache htdocs]# apachectl restart #重启apache以便生效
Indexes 的作用就是当该目录下没有 index.html 文件时,就显示目录结构。
客户端再次访问:

OK!!!看到的是403页面。 四、隐藏apache的版本信息
[root@apache htdocs]# curl -I 127.0.0.1 #查看默认apache的状态信息 HTTP/1.1 403 Forbidden Date: Sun, 13 Oct 2019 03:08:39 GMT 'Server: Apache/2.4.23 (Unix) ' #可以看到apache的详细版本信息 Content-Type: text/html; charset=iso-8859-1
若想隐藏,须进行以下操作:
[root@apache htdocs]# cd ../conf/ [root@apache conf]# vim httpd.conf #编辑主配置文件 Include conf/extra/httpd-default.conf #去掉此行前的注释符号后保存退出 [root@apache conf]# pwd #查看当前工作路径 /usr/local/http-2.4.23/conf [root@apache conf]# vim extra/httpd-default.conf #编辑此文件 #找到下面这两行: ServerTokens Full ServerSignature Off #更改如下: ServerTokens Prod ServerSignature On #更改后保存退出 [root@apache conf]# apachectl restart #重启服务,以便更改生效 [root@apache conf]# curl -I 127.0.0.1 #再次访问查看 HTTP/1.1 403 Forbidden Date: Sun, 13 Oct 2019 03:19:17 GMT Server: Apache #发现只有apache了,而没有了详细的版本 Content-Type: text/html; charset=iso-8859-1
如果你需要彻底将版本之类的信息进行改头换面,你就需要在编译之前做准备或者进行重新编译了。在重新编译时,修改apache的源码包下 include 目录下的 ap_release.h 配置文件
#define AP_SERVER_BASEVENDOR "Apache Software Foundation" #服务的供应商名称 #define AP_SERVER_BASEPROJECT "Apache HTTP Server" #服务的项目名称 #define AP_SERVER_BASEPRODUCT "Apache" #服务的产品名 #define AP_SERVER_MAJORVERSION_NUMBER 2 #主要版本号 #define AP_SERVER_MINORVERSION_NUMBER 4 #小版本号 #define AP_SERVER_PATCHLEVEL_NUMBER 23 #补丁级别 #define AP_SERVER_DEVBUILD_BOOLEAN 0 #
上述列出的行,可以修改成自己想要的,然后编译安装之后,客户端就彻底不知道你的版本号了。 五、apache日志切割 随着网站的访问量越来越大,web server产生的日志文件也会越来越大,如果不进行分割,必定会有一定程度的不方便。因此,管理好这些海量的日志对网站的意义是很大的。
日志分割共两种方法。
方法1:使用rotatelogs(apache自带的工具)按天分割日志,每隔一天记录一个日志
[root@apache conf]# vim httpd.conf #编辑主配置文件 #将以下两行配置注释掉(去除默认的日志记录) #ErrorLog "logs/error_log" #CustomLog "logs/access_log" common #然后最好在CustomLog "logs/access_log" common配置的下一行添加如下内容(以下内容不可以直接复制,请看下面的解释): ErrorLog "|/usr/local/http-2.4.23/bin/rotatelogs -l logs/error_%Y-%m-%d.log 86400" CustomLog "|/usr/local/http-2.4.23/bin/rotatelogs -l logs/access_%Y-%m-%d.log 86400" combined #添加后,保存退出即可
上面两行的配置项,必须在<IfModule log_config_module> </IfModule>标签中写入。这就是我为什么说最好写在CustomLog "logs/access_log" common配置的下一行的原因,因为这行就在该标签中,写在它的下一行,错不了。
在上面添加的内容中,86400为轮转的时间,单位是秒(也就是一天生成一个日志文件); 需要注意我这里的rotatelogs命令的绝对路径,需根据自己的实际安装路径来定,不要直接复制。
[root@apache conf]# apachectl restart #重启服务,以便更改生效 [root@apache conf]# ls ../logs/ #查看日志文件,会发现没有access的切割日志 access_log 'error_2019-10-13.log' error_log httpd.pid #此时,原来的access_log和error_log日志文件已经可以删除了 #没有access的切割日志是因为更改后还没有访问过 [root@apache conf]# curl 127.0.0.1 &> /dev/null #访问一下 [root@apache conf]# ls ../logs/ #再次查看,就有了 'access_2019-10-13.log error_2019-10-13.log' httpd.pid access_log error_log
由于 apache 自带的日志轮询工具 rotatelogs,据说在进行日志切割时容易丢日志,因此我们通常使用 cronolog (也就是方法2)进行日志轮询。 方法2:使用cronolog为每一天建立一个新的日志 同样需要注释掉主配置文件中的下面两行:
#ErrorLog "logs/error_log" #CustomLog "logs/access_log" common
cronolog包: https://pan.baidu.com/s/1dg9khnNYUbIA4j31h2VYbA 提取码: 5jg5
[root@apache src]# rz #使用xshell上传我提供的源码包 #进行编译安装 [root@apache src]# tar zxf cronolog-1.6.2.tar.gz [root@apache src]# cd cronolog-1.6.2/ [root@apache cronolog-1.6.2]# ./configure && make && make install [root@apache conf]# pwd #切换工作路径至此 /usr/local/http-2.4.23/conf [root@apache conf]# vim httpd.conf #编辑主配置文件 #将方法1中写入的日志切割配置项删除,写入下面的两行配置 ErrorLog "|/usr/local/sbin/cronolog logs/error-%Y-%m-%d.log" CustomLog "|/usr/local/sbin/cronolog logs/access-%Y-%m-%d.log" combined #写完保存退出即可 [root@apache logs]# ls #为了避免混乱,我移走了原有的日志文件 httpd.pid [root@apache logs]# apachectl restart #重启 [root@apache logs]# curl 127.0.0.1 &> /dev/null #访问一下,以便生成访问日志文件 [root@apache logs]# ls #查看确认, access-2019-10-13.log error-2019-10-13.log httpd.pid
至此,即可实现了每天的日志文件分开单独存放。
如果 Apache 中有多个虚拟主机,最好每个虚拟主机中放置一个这样的代码,并将日志文件名改成不同的名字。 3、附加 如果网站访问量实在过于庞大,那么我们可能更需要的是将日志按小时分割,然后按小时分割的日志存放在一个目录中,也就是说,每天对应一个目录,这个 目录下存放的是当天产生的按小时分割的日志。
实现如下:
只需将方法2中写入的两行配置项,更改为如下即可:
[root@apache conf]# vim httpd.conf #更改如下 ErrorLog "|/usr/local/sbin/cronolog logs/error_%Y-%m-%d/error_log.%H" CustomLog "|/usr/local/sbin/cronolog logs/access_%Y-%m-%d/access_log.%H" combined [root@apache conf]# apachectl restart #重启 [root@apache logs]# curl 127.0.0.1 #访问一下 [root@apache http-2.4.23]# pwd #切换工作路径 /usr/local/http-2.4.23 [root@apache http-2.4.23]# tree logs/ #使用tree命令查看 logs/ |-- access_2019-10-13 #访问的日志日期 | `-- access_log.12 #12点产生的 |-- error_2019-10-13 #错误的日志日期 | `-- error_log.12 #也是12点产生的 `-- httpd.pid
注意:以上两个管道日志文件程序还有一点不同之处是使用 cronolog 时如果日志是放在某个不存在的路径则会自动创建目录,而使用 rotatelogs 时不能自动创建,这一点要特别注意 六、配置防盗链 有时候,你的网站莫名其妙的访问量变大,不要高兴的太早,有可能是被别人盗链了。 举个例子:比如你搭了个论坛,里面有些热点图片、视频;然后别人将他网站上访问图片的地址重定向到你的 论坛上,这样他的服务器就可以空闲出来了;也就是说别人访问他网站的图片视频,消耗的却是你服务器的资源。 如果解决这个问题,需要借助apache的rewrite模块,配置如下:
[root@apache conf]# vim httpd.conf #编辑主配置文件 #确认有以下的配置项,并且去掉注释,若没有下面这行,则需安装rewrite模块 LoadModule rewrite_module modules/mod_rewrite.so
开启rewrite模块后,找到自己网站对应的配置文件(如主配置文件或虚拟主机配置文件中),在末尾加入以下代码:
RewriteEngine On RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://test.com/.*$ [NC] RewriteCond %{HTTP_REFERER} !^http://test.com$ [NC] RewriteCond %{HTTP_REFERER} !^http://www.test.com/.*$ [NC] RewriteCond %{HTTP_REFERER} !^http://www.test.com$ [NC] RewriteRule .*.(gif|jpg|swf)$ http://www.test.com/about/nolink.png [R,NC,L]
相关选项解释如下:
- RewriteEngine On:启用rewrite,必须写上;
- RewriteCond……:在写RewriteRule之前,可以有一条或多条,用于测试rewrite的匹配条件,具体写法,后面再聊;
- .RewriteRule:配置规则;
- %{HTTP_REFERER}:服务器变量,http referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上referer,告诉服务器是我是从哪个页面链接过来的,服务器就借此可以获取一些信息用于处理,比如从我主页上链接到一个朋友那里,他的服务器就可以从http referer中统计出每天有多少用户点击我主页上的链接访问他的网站;
- [ NC]指的是不区分大小写,[R]强制重定向 redirect;
- 字母 L 表示如果能匹配本条规则,那么本条规则是最后一条(Last),忽略之后的规则;
- RewriteCond %{HTTP_REFERER} !^$:这行配置项作用是允许空“HTTP_REFERER”的访问,就是用户直接在浏览器输入URL访问该资源,而不是通过链接访问的。
- RewriteCond %{HTTP_REFERER} !test.com/.$ [NC]和RewriteCond %{HTTP_REFERER} !www.test.com/.$ [NC]是设置允许访问的HTTP来源,包括网站自身。
- RewriteRule .*.(gif|jpg|swf)$ http://www.test.com/about/nolink.png [R,NC,L] 的作用是将不满足referer条件的访问重定向至nolink.png, nolink.png 位于允许“盗链”的目录 about中,要相当注意,不然,警告信息和图片将无法在对方网站上显示。
注意:测试时注意清除缓存。
小结:

1、红色部分: 表示自己的信任站点。对我的站点来说,设置为 http://www.test.com 和http://test.com 2.、绿色部分: 要保护文件的扩展名(以|分开)。表示以这些为扩展名的文件,必须通过红色标注的网址引用,才可以访问。 3、蓝色部分: 定义被盗链时替代的图片,让所有盗链 jpg、gif、swf 等文件的网页,显示网页文档根目录下的 about/ nolink.png 文件。 注意:替换显示的图片不要放在设置防盗链的目录中,并 且该图片文件体积越小越好。当然你也可以不设置替换图片,而是使用这条语句即可:RewriteRule .*.(gif|jpg|png)$ – [F] 注:[F] (强制 URL 为被禁止的 forbidden),强制当前 URL 为被禁止的,即,立即反馈一个 HTTP 响应代码 403(被禁止的)。
对上述的防盗链进行测试: 我这里有A( www.test.com ) 和B( www.daolian.com ) 两台服务器,其中B服务器的index.html首页文件如下:
[root@localhost html]# cat index.html #B 服务器的首页文件是个链接,指向了A服务器的test.jpg文件。 <a href="http://www.test.com/test.jpg">link</a>
client直接访问A服务器的test.jpg文件时(其URL和B服务器的超链接地址一样),得到以下美女一枚:

但是如果通过B服务器的超链接来访问呢?请继续看下去。

What???怎么是只二哈,不应该是一枚美女吗?并且它的URL也不是我们B服务器指定的超链接地址了。

这就是我们A服务器的防盗链配置生效了!!!
利用rewrite实现Apache防盗链小结
通过判断referer变量的值,来判断图片或资源的引用是否合法,只有在根据配置符合设定需求范围内的referer,这样的网站内容,才能调用访问指定的资源内容,从而实现了资源被网站盗链的目的。需要注意的是:是所有的用户代理(浏览器)都会设置referer变量,而且有的还可以手工修改erferer,referer是可以被伪造的,上面的配置只是一种简单的防护手段。应付一般的盗链足矣。
当网站被盗链,一般可以采取以下措施:
- 对本站的图片、视频、音频等文件标上自己的站名品牌或者相关水印;
- 设置防火墙,从源头IP进行控制
- 设置防盗链(根据referer机制)
网站被非法盗链使用,会导致网站带宽成本加大以及服务器压力加大,严重时会导致巨额的网站及正常用户访问受到影响。