Nginx配置HTTPS简介

申请证书

对于个人网站,建议使用Let’s Encrypt免费证书,目前Let’s Encrypt ACME v2 已正式支持通配符证书。
申请教程:Let’s Encrypt 免费通配符 SSL 证书申请教程

Nginx 配置证书

基础配置

证书申请好后,现在就开始配置了,示例配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
listen 443;
listen [::]:443;
server_name example.mango.im;

ssl on;
ssl_certificate /etc/nginx/ssl/example.pem;
ssl_certificate_key /etc/nginx/ssl/example.key;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;

location / {
proxy_pass http://localhost:12345;
}
}

ssl_ciphers有一些弱加密,比较完整一个:

1
2
# Disable all weak ciphers
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

HSTS

以上配置了端口443,但一般情况下没有用户会直接输入https://,所以还是要配置80端口进行重定向

1
2
3
4
5
server {
listen 80;
server_name example.mango.im;
return 301 https://example.mango.im$request_uri;
}

这时访问example.mango.im即可跳转到https://example.mango.im,但这里还存在一个问题,聪明如你,对,当80到443跳转时依然可以被劫持、伪造。

所以这里介绍一下HSTS,什么是HSTS?

HTTP严格传输安全(英语:HTTP Strict Transport Security,缩写:HSTS)是一套由互联网工程任务组发布的互联网安全策略机制。网站可以选择使用HSTS策略,来让浏览器强制使用HTTPS与网站进行通信,以减少会话劫持风险.

HSTS的作用是强制客户端(如浏览器)使用HTTPS与服务器创建连接。服务器开启HSTS的方法是,当客户端通过HTTPS发出请求时,在服务器返回的超文本传输协议(HTTP)响应头中包含Strict-Transport-Security字段。非加密传输时设置的HSTS字段无效。

比如,https://example.com/ 的响应头含有Strict-Transport-Security: max-age=31536000; includeSubDomains。这意味着两点:

在接下来的31536000秒(即一年)中,浏览器向example.com或其子域名发送HTTP请求时,必须采用HTTPS来发起连接。比如,用户点击超链接或在地址栏输入 http://www.example.com/ ,浏览器应当自动将 http 转写成 https,然后直接向 https://www.example.com/ 发送请求。
在接下来的一年中,如果 example.com 服务器发送的TLS证书无效,用户不能忽略浏览器警告继续访问网站。

一般配置:

1
2
3
4
5
6
server {
listen 443 ssl;
server_name www.example.com;

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

参考

阿里云OSS搭建静态网站

迫于想的太多,做的太少,买了几年的阿里云ECS也没有作出什么东西来,最近要到期了,准备放弃续费。但是之前备案的域名不想被审核掉(PS im域名现在已经不好备案了)。看了下阿里云的便宜的虚拟主机也是大几百,不太符合我的需求。

最开始是淘宝找了家有出口阿里云IP的虚拟主机,几十块吧,买了发现也就是能放个静态页面,管理面板非常老旧。今儿偶尔得知OSS也是可以挂个静态页面,域名CNAME解析过去就行,关键是OSS非常便宜,9块钱一年的存储空间,流量可以按量计费,如果只放个静态HTML其实用不了多少,图片等资源可以放到七牛上,免费的流量够用了。

顺手搭建了一个简单的JSON在线解析:http://www.mango.im/json.html

PHP增加X_REQUEST_ID

最近项目中需记录每个请求的request_id,从nginx追踪到php

这里使用nginx内置的变量$request_id(nginx 1.11.0 版本新增加的feature)

修改fastcgi_params增加

1
fastcgi_param X_REQUEST_ID $request_id;

这时通过打印全局变量$_SERVER即可看到我们刚刚定义的X_REQUEST_ID

获得X_REQUEST_ID后就可以用在具体的业务场景中了,比如我用Laravel记录日志,其中保存X_REQUEST_ID

截取部分代码:

1
2
3
4
5
6
7
8
9
10
$logger = new Logger('mango');
// 额外增加x_request_id
$logger->pushProcessor(function ($record) {
$record['extra']['x_request_id'] = app('request')->server('X_REQUEST_ID');
return $record;
});

$w = new Writer($logger);
$w->useDailyFiles(storage_path()."/logs/".$file.'.log');
$w->info($message = $addr, $context = $info);

补充:

如果要给laravel默认错误日志也加上X_REQUEST_ID,可以参考以下代码,路径:/bootstrap/app.php laravel5.5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/

// 日志增加x_request_id
$app->configureMonologUsing(function($monolog) {
$monolog->pushProcessor(function ($record) {
$record['extra']['x_request_id'] = app('request')->server('X_REQUEST_ID');
return $record;
});

$filename = storage_path('/logs/laravel.log');
$monolog->pushHandler(new Monolog\Handler\RotatingFileHandler($filename));
});

return $app;

Laravel 社会化登录简介

socialite作为官方包使用起来非常方便,目前支持大多数的社会化登录网站,比如微信,微博,Github等等。

具体支持哪些网站,参见列表:https://socialiteproviders.github.io/about.html

比如网站需要对接微信web授权登录,使用 https://socialiteproviders.github.io/providers/weixin.html 即可,文档已经写的比较完善,这里不再赘述。

举个微信实际例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Http\Controllers\WeChat;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Laravel\Socialite\Facades\Socialite;


class WeChatController extends Controller
{
public function oauth(Request $request)
{
$url = $request->input('url', ''); // 来源url
return Socialite::with('weixin')->redirectUrl(url("wechat/auth/callback?url=".urlencode($url)))->redirect();
}

public function callback(Request $request)
{
$oauthUser = Socialite::with('weixin')->user();
$request->session()->put('user_wechat', $oauthUser->user);
return redirect($request->input('url'));
}
}

在中间件中判断session是否存在,不存在调用oauth接口即可。可以说灰常方便了😀

记一次nginx代理配置

背景:

最近部署一台测试服务器在外网,主要用于微信等三方对接必须外网环境,然而数据库等依赖均在内网。

方案 1

打通外网服务器到内网VPN,所依赖的内网服务全走VPN到内网,由于数据库多条SQL执行,每次执行都要经历一次外网到内网的延时,速度实在无法忍受。

方案 2

由外网服务器做代理,转发到内网,所有依赖在内网中解决。

做了张简单的图

代理流程图
image

外网nginx配置

1
2
3
4
5
6
7
8
9
10
11
12
server{
listen 443 ssl;
server_name test.com;
# 此处省略ssl的配置
location / {
# 内网的IP
proxy_pass http://192.168.8.12:81;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

内网nginx配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 81;
server_name localhost;

charset utf-8;

root /var/www/test;

location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

配置好nginx还需要配置fastcgi参数,这里只列出改动项,根据自己需求更改。

1
2
3
4
fastcgi_param  REQUEST_SCHEME     https;
fastcgi_param HTTPS on;
fastcgi_param SERVER_PORT 443;
fastcgi_param SERVER_NAME test.com;

php7性能测试扩展php-xhprof-extension初探

最近发现测试环境接口响应巨慢,为了验证是哪里出了问题,遂引入性能测试。由于使用的php7,目前可用的性能测试扩展似乎只有php-xhprof-extension可选。

下载安装:https://tideways.com/profiler/downloads

以下为centos安装方法

1
2
3
4
5
6
echo "[tideways]
name = Tideways
baseurl = https://s3-eu-west-1.amazonaws.com/qafoo-profiler/rpm" > /etc/yum.repos.d/tideways.repo
rpm --import https://s3-eu-west-1.amazonaws.com/qafoo-profiler/packages/EEB5E8F4.gpg
yum makecache --disablerepo=* --enablerepo=tideways
yum install tideways-php tideways-cli tideways-daemon安装

安装好后,重启php-fpm

1
service php-fpm restart

如报异常:

Module ‘tideways’ already loaded in Unknown on line 0
可能是tideways重复加载了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

[root@test]# php -i | grep .ini$
Loaded Configuration File => /etc/php.ini
/etc/php.d/tideways.ini
user_ini.filename => .user.ini => .user.ini
vi /etc/php.d/tideways.ini
```
注释掉:extension=tideways.so

这时检查下扩展是否已经引用

php -m | grep tide
tideways
模块就做好了。

下面开始使用,官网文档使用是这样的:

```php
<?php

tideways_xhprof_enable();

my_application();

$data = tideways_xhprof_disable();

file_put_contents("/tmp/profile.xhprof", serialize($data));

其实就是两个函数的使用,但是当你使用时会报错,什么错误呢

PHP Fatal error: Uncaught Error: Call to undefined function tideways_xhprof_disable()
再次确认下tideways扩展有没有装好,可以用phpinfo()看一下。发现已经引入了模块。

这就很奇怪了。

再一番Google后,找到了这个问答的评论部分 https://stackoverflow.com/questions/38103784/how-to-use-tideways-on-own-server

Thank you for this. The projects own documentation contains incorrect commands (i.e. tideways_xhprof_enable() and tideways_xhprof_disable()) which meant all I could ever get was fatal errors. Remove the _xhprof token and it all works 🙂

啥意思呢?

就是把函数tideways_xhprof_enable();中的_xhprof去掉就可以了。

然后本人试了下,改成这样

1
2
3
4
5
6
7
8
9
<?php

tideways_enable();

echo "test";

$data = tideways_disable();

file_put_contents("/tmp/profile.xhprof", serialize($data));

这时就可以了,原来方法名都已经改了😳

运行完后可以读取文件,然后反序列化就可以拿到分析结果了。

类似这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<?php

array(
"main()" => array(
"wt" => 1000,
"ct" => 1,
"cpu" => 400,
),
"main()==>foo" => array(
"wt" => 500,
"ct" => 2,
"cpu" => 200,
),
"foo==>bar" => array(
"wt" => 200,
"ct" => 10,
"cpu" => 100,
),
)

关于对数据的处理和展示,后续有空的时候更新。

Laravel使用phpCAS注意点

项目使用框架为Laravel5.5,CAS客户端为https://github.com/apereo/phpCAS
注意事项:

具体使用可以参考phpCAS的docs/examples

附上一张CAS流程图:

image

开启OPcache加速你的PHP应用

什么是OPcache?

Zend OPcache 通过 opcode 缓存和优化提供更快的php执行过程。它将预编译的脚本文件存储在共享内存中供以后使用,从而避免了从磁盘读取代码并进行编译的时间消耗。同时,它还应用了一些代码优化模式,使得代码执行更快。

为什么要用?

如下图php的执行的生命周期,第一部分是没有opcode缓存的情况。第二部分则是缓存了opcode的情况。

php_exec_process

从图中我们可以清楚的看到,PHP开始执行请求->读文件->词法分析->语法分析->生成opcode->再由Zend引擎执行opcode返回执行结果。

聪明如你肯定发现在php文件没有更改的情况下,每次都要经过词法分析、语法分析、生成opcode有点浪费。那在代码没有更改的时候能不能把要执行代码的opcode缓存起来用于下次使用呢?这样不就提高了速度么。

OPcache就是来管理opcode缓存的,如上图下半部分。

是共享内存么?

PHP官方文档:
PHP processes with opcode cache enabled use shared memory for opcode caching. Yet, PHP processes will be able to “share” that shared memory, only if they were all created (forked) from the same, original PHP process, that allocated that shared memory.

据本人的实验,开启OPcache后php-fpm的主进程内存占用会增加,即是把生成的opcode放在主进程里了。

补充一条查看php进程内存使用命令:

查看php-fpm的进程个数

1
ps -fe |grep "php-fpm"|grep "pool"|wc -l

查看单个进程内存占用大小

1
ps -ylC php-fpm --sort:rss

RSS 为内存大小,单位K

性能有多大提升?

本热在Ubuntu php7.0下开启OPcache的情况下速度可以提升13%左右,目前用在测试环境中,还在观察。

如何配置?

官方文档讲的很清楚,这里不作赘述。
http://php.net/manual/zh/opcache.configuration.php#ini.opcache.max-wasted-percentage

如何发布?

手动重置:

如果validate_timestamps 选项设置很久的话,就需要手工重置opcode。

PHP提供了两个方法:
opcode_reset() 重置整个字节码缓存内容
opcache_invalidate 废除指定文件opcode缓存

自动更新:

validate_timestamps 设置5秒的话,在代码更新好后等个5秒就好了。

重启php-fpm,不太建议这么做。

相关工具