HTTP状态码
本篇文章主要介绍运维过程中经常遇到的状态码,并通过业界流行的Nginx进行模拟实现。
2XX状态码
2XX类型状态码表示一个HTTP请求成功,最典型的就是200
# 200状态码
# 这个使我们最希望看到的状态码,表示一个HTTP请求得到了正确的响应,在此不做模拟测试
3XX状态码
# 3XX 类型状态码主要表示HTTP请求URL重定向行为,最常见的3XX状态码有301,302,304
301
测试用例
# URL: 永久性重定向,在Nginx中通过rewrite指令结合permanent标记实现.
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
rewrite ^(.*)$ /index.html permanent;
location / {
index index.html index.htm;
}
}
curl 127.0.0.1 -I
# TTP/1.1 301 Moved Permanently
302
测试用例
# URL: 临时重定向,在Nginx通过rewrite指令结合redirect标记实现
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
rewrite ^(.*)$ /index.html redirect;
location / {
index index.html index.htm;
}
}
curl 127.0.0.1 -I
# HTTP/1.1 302 Moved Temporarily
301 VS 302
# 301和302状态码都是URL重定向。其中301永久重定向,302临时重定向。不管是永久还是临时,但对⽤户⽽⾔这两者没有任何感官上的区别。都是在访问连接A的时候跳转到了连接B,并看到浏览器上的地址同样由A变成了B。既然如此,那为什么还要同时存在301和302呢?
# 它们主要区别在于搜索引擎。搜索引擎是要建⽴索引规则和权重的,如果连接A被设定为永久重定向到连接B,那搜索引擎可以确定A的地址永久改变了,就会把B当做唯⼀有效的⽬标地址。这时搜索引擎会把⽼地址相关信息带到新地址,同时在搜索引擎索引库中彻底废弃掉原先的⽼地址。⽽搜索引擎对于302则没有这样的⾏为。
304
测试用例
# 客户端缓存,通过Nginx中的expires指令完成
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|html)$ {
expires 1d;
}
}
# 当客户端浏览器经过第一次访问后,第二次访问时只要访问资源没有过期,状态码就为304,说明使用了上一次请求时候的响应资源
4XX状态码
# 4XX 类型状态码主要表示客户端错误。客户端发起的HTTP请求头不完整、客户度使⽤的⽤户名、密码错误等等。都会被标示为客户端错误。常⻅的4XX状态码有 400、401、403等。
400
# 当WEB服务器遇到不完整的HTTP请求头时,会返回400状态码。请求头信息或者Cookie信息过⼤,通常是导致不完整的请求头的原因。那多⼤的请求头算过⼤呢?
# 这个值由WEB服务器端配置决定。在Nginx 这类WEB服务器中,决定这个值的配置字段是large_client_header_buffers。 我们通过调⼩这个字段,来模拟400状态码。
测试用例
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# 此指令在NGINX的HTTP段和SERVER段都可以⽣效。
# 若实验发现在SERVER段中不⽣效,可以在HTTP段中设置。
large_client_header_buffers 1 1k;
location / {
index index.html index.htm;
}
}
# 测试
# 设置一个远超过1K的cookie
curl --cookie "user=sidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidisidisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidisidisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidisidisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidisidisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisisisisisisisisisisisisisisisisisisisisisisisisisisisisisisisisisisissisisisisisisisisisisisisisiisissiissisisisiissi" 127.0.0.1 -I
HTTP/1.1 400 Bad Request
Server: nginx/1.18.0
Date: Mon, 04 May 2019 11:05:55 GMT
Content-Type: text/html
Content-Length: 233
Connection: close
401
# 权限验证错误。就是需要⽤户名、密码等权限认证,但是客户端⼜没有通过认证。在Nginx中要想模拟这种状态码,必须将Nginx调整成认证模式。
测试用例
生成授权用户名和密码
htpasswd -c /etc/passwd.db youmen
# Nginx授权配置
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
auth_basic "secret";
auth_basic_user_file /etc/passwd.db;
index index.html index.htm;
}
}
# 测试
# test.com 在本地hosts文件中进行绑定,然后开浏览器输入正确用户名和密码访问
403
# 没有权限访问,文件权限过小或者认为设置不允许某个IP地址访问等,都会浮现403状态码
测试用例
# 将⽂件权限设置为最⼩
# ll /usr/share/nginx/html/index.html
-rw-r--r-- 1 root root 3700 3⽉ 6 04:26 /usr/share/nginx/html/index.html
# chmod 0 /usr/share/nginx/html/index.html
# ll /usr/share/nginx/html/index.html
---------- 1 root root 3700 3⽉ 6 04:26 /usr/share/nginx/html/index.html
# Nginx配置文件
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
index index.html index.htm;
}
}
curl http://127.0.0.1/index.html -I
# HTTP/1.1 403 Forbidden
404
# 文件不存在,当我们访问一个不存在文件时,就会出现这个错误
# 还原403状态码配置属主属组,随意输入URL访问
curl http://127.0.0.1/youmen -I
# HTTP/1.1 404 Not Found
499
# 499这个状态码并不是http协议中定义的标准状态码,⽽是Nginx⾃⼰定义的⼀个状态码。当客户端主动断开连接的时候,Nginx就会返回499的状态码。按照这个状态码的定义,只要在Nginx返回结果之前主动断开客户端连接,就应该会复现这个状态码。
测试用例
# 创建一个PHP脚本,并将脚本放在Nginx的root目录中,以此脚本模拟一个很长时间的响应
cat sleep.php
# 开启PHP-FPM服务
systemctl start php-fpm
# 测试Nginx配置文件
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|html)$ {
expires 1d;
}
location / {
# 确保 fastcgi.conf 中存在 SCRIPT_FILENAME 配置, 以下5XX案例中也要确保存
在
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi.conf;
# PHP-FPM 默认开启了9000端⼝。若做了个性化配置,请⼿动调整。
fastcgi_pass 127.0.0.1:9000;
}
}
# 测试
# 不要等程序正常结束,CTRL + C直接退出
curl http://127.0.0.1/sleep.php -I
^C
# 在CURL模拟测试同时查看access.log
tail -f /var/log/nginx/access.log
127.0.0.1 - - [13/May/2019:23:24:33 -0400] "HEAD /sleep.php HTTP/1.1" 499 0
"-" "curl/7.29.0"
5XX状态码
# 5XX 类型状态码主要表示服务端错误,此时不要纠结客户端问题了
500
一般有以下几种情况
测试用例
# 将Nginx 的root ⽬录中的sleep.php 随意个更改,让它的PHP语法出现问题。
# cat sleep.php
# 测试Nginx配置文件
server {
listen 80;
server_name test.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|html)$ {
expires 1d;
}
location / {
# 确保 fastcgi.conf 中存在 SCRIPT_FILENAME 配置, 以下5XX案例中也要确保存
在
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi.conf;
# PHP-FPM 默认开启了9000端⼝。若做了个性化配置,请⼿动调整。
fastcgi_pass 127.0.0.1:9000;
}
}
curl http://127.0.0.1/sleep.php -I
# HTTP/1.1 500 Internal Server Error
# 1.web脚本错误,如php语法错误,lua语法错误等.
# 2.访问量大的时候,由于系统资源限制,而不能打开过多的文件.
# 一般分析思路:
# 查看nginx error log ,查看php error log
# 如果是too many open files,修改nginx的worker_rlimit_nofile参数,
# 使用ulimit查看系统打开文件限制,修改/etc/security/limits.conf
# 如果是脚本的问题,则需要修复脚本错误,并优化代码
# 各种优化都做好,还是出现too many open files,
# 那就要考虑做负载均衡,把流量分散到不同服务器上去了
错误原因总结:
1.硬盘空间满了
使用 df -k 查看硬盘空间是否满了。清理硬盘空间就可以解决500错误。nginx如果开启了access log,在不需要的情况下,最好关闭access log。access log会占用大量硬盘空间。
2.nginx配置文件错误
这里不是指语法错误,nginx如果配置文件有语法错误,启动的时候就会提示。当配置rewrite的时候,有些规则处理不当会出现500错误,请仔细检查自己的rewrite规则。如果配置文件里有些变量设置不当,也会出现500错误,比如引用了一个没有值的变量。
3.如果上面的问题都不存在可能是模拟的并发数太多了,需要调整一下nginx.conf的并发设置数
3 . 系统打开文件限制
解决方法是:
vim /etc/security/limits.conf
soft nofile 65535
hard nofile 65535
# 再打开/etc/nginx/nginx.conf
# 在worker_processes下面增加一行
vim /etc/nginx/nginx.conf
worker_rlimit_nofile 65535;
systemctl reload nginx
kill -9 `ps -ef | grep php | grep -v grep | awk '{print $2}'`
/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -C 100 -u www-data -f /usr/bin/php-cgi
killall -HUP nginx
# 重启后再看nginx的错
重启后再看nginx的错误日志,也没有发现500报错的情况了。
有可能是数据库问题我的在nginx日志php日志都没有发现什么问题, 最后发现数据库访问不了,修正后问题解决。
502
# 502 Bad Geteway。Nginx 502错误的原因⽐较多,主要是因为在代理模式下后端服务器出现问题引起的。这些错误⼀般都不是Nginx本身的问题,⼀定要从后端找原因。⽐如这⾥复现⼀种后端PHP-FPM进程挂掉的情况,就会出现502错误
模拟
# 先将后端的PHP-FPM进程关闭。
# systemctl stop php-fpm
# 模拟测试
curl http://127.0.0.1/sleep.php -I
# HTTP/1.1 502 Bad Gateway
是指请求的php-fpm已经执行,但是由于某种原因没有执行完毕,最终导致php-fpm进程终止,错误原因比较多,一般不是Nginx本身,从后端找原因,比如PHP挂掉;
对于PHP,造成502原因常见的就是脚本执行时间超过了Timeout设置时间,或者设置过大,导致PHP长时间不能释放,没有worker进程出来接请求;
适当增加PHP执行时间,先清除502,优化要花费更多时间;
控制php执行时间选型有两个,
1> 在php.ini中max_execution(厄涩Q熏)_timeout
2> 也有可能跟php执行程序性能有关,网站访问量大,而php-cgi的进程数偏少,针对这种情况,只需要增加php-cgi的进程数,将php-fpm.conf中的max_children值适当增加.这个数据根据你的VPS或独立服务器的配置进行设置,一般一个php-cgi进程占20M内存,自己算算适量增多.
503
服务暂时不可用
由于临时服务器维护或者过载,服务器当前无法处理请求,这个状态是临时的,并且将在一段时间以后恢复;
多半是因为网站访问量过大,造成流量请求过多,Nginx不向后端转发了,或者Upstream的地址,端口问题,先检查CPU,内存,负载特别高,如果没有检查配置;
升级空间到更佳配置,要么检查网站系统程序使之更佳;
单个IP并发设置过小会导致503报错.
504
# 504 Gateway Time-out。顾名思义,就是超时了。当PHP-FPM的执⾏时间⼤于Nginx的读超时间,此时就会出现 504状态码。
# 将上面示例中脚本中的时间超过php-fpm最大超时时间就行了
服务器作为网关或代理,但是没有及时从上游服务器收到请求。 就是客户端发出请求没有到达网关,请求没有到可以执行的php-fpm;
# 1>可能与Nginx.conf配置有关系,Nginx连接超时时间;
# 2>当PHP-FPM执行时间超过大于Nginx的读超时时间;
小结
1XX # 表示临时响应并需要请求者继续执行操作的状态码
200 # 表示一个HTTP请求得到了正确响应
301 # 永久重定向
302 # 临时重定向
401 # 需要授权
403 # 访问被拒绝,没有权限
404 # 页面找不到,服务器找不到请求页面
410 # 如果请求资源已被永久删除,服务器会返回此响应
500 # 服务器内部错误