Nginx 性能优化及反向代理 NextCloud

Published on: April 29, 2022 | Reading Time: 4 min

nginx
性能优化
nextcloud
反向代理

Nginx 是一个异步框架的网页服务器,具有占用内存少、稳定性高、可大量并行处理等特点。截止 2018 年,全球有 30.46% 的网站使用 Nginx 作为代理。除了网页代理外,Nginx 也可作为反向代理、负载均衡等应用,本站使用的网页服务器就是 Nginx。

我的 Nextcloud 私有云是搭建在 HostHatch 洛杉矶数据中心的大容量机械硬盘 VPS 上。由于性能有限加上 HostHatch 没有对中国路由进行特别优化,导致国内直连的情况下丢包严重,连接状态极差。于是我用狗云圣何塞机房 4837 线路的 VPS 作为 Nextcloud 的中转来解决访问速度的问题,同时把本站也搭建在狗云的VPS上就不会有访问困难的问题了。这两种需求利用 Nginx 都能很好的满足。

我 Nginx 配置的基本架构:

  • Nginx 提供本站的网页服务,并处理本站和 Nextcloud 的 HTTPS 加密。
  • Nginx 为 Nextcloud 做反向代理,使客户端访问 Nextcloud 经过网络更好的 Nginx 所在的服务器来加速访问。

优化要点:

  • 合理的配置本地缓存
  • 开启压缩并做适当的配置
  • 反向代理服务器不要缓存后端的数据,只作为一个桥梁来提供中转服务。这样数据传输更加直接,同时也能减轻反向代理服务器的压力。(我的反向代理服务器只有 5G 的硬盘,如果开启反向代理缓存的话,向后端传输数据的最大文件大小会受这个缓存的限制)
  • HTTPS 加密方式的选择

我的nginx.conf配置

user  site site; #用户和用户组
worker_processes  auto; #CPU 核心数
worker_rlimit_nofile  8192; #最大打开文件数量

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

events {
    use epoll; #epoll事件模型
    worker_connections  1024; #单个 worker 进程允许客户端最大连接数
    multi_accept on; #worker 按串行方式来处理连接
}

http {
    include       /etc/nginx/mime.types; #媒体类型
    default_type  application/octet-stream; #媒体类型
    charset       UTF-8;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    # 安全
#   add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
#   add_header X-Robots-Tag none;
#   add_header X-Download-Options noopen;
#   add_header X-Permitted-Cross-Domain-Policies none;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
#   add_header X-Frame-Options SAMEORIGIN; #不允许页面从框架 frame 或 iframe 中显示

    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets on;
    ssl_stapling on;
    ssl_stapling_verify on;

    sendfile        on; #开启高效文件传输模式
    tcp_nopush      on; #防止网路阻塞
    keepalive_timeout  65; #客户端连接保持会话超时时间
    tcp_nodelay     on; #防止网络阻塞
    types_hash_max_size 2048;
    client_header_buffer_size  4096; #客户端请求头部的缓冲区大小,getconf PAGESIZE 命令查看
    open_file_cache max=8192 inactive=20s; #打开文件指定缓存,建议和打开文件数一致,inactive 经过多长时间文件没被请求后删除缓存
    open_file_cache_valid 30s; #多长时间检查一次缓存的有效信息
    open_file_cache_min_uses 1; #open_file_cache 指令中的 inactive 时间内文件的最少使用次数,1次都没用,删除
    client_header_timeout 15; #设置请求头的超时时间,超过这个时间没有发送任何数据,返回 request time out 错误
    client_body_timeout 15; #设置请求体的超时时间
    reset_timedout_connection on; #关闭不响应的 client 连接,释放那个 client 所占有的内存
    send_timeout 15; #超过这个时间,客户端没有任何活动,nginx 关闭连接
    server_tokens off; #关闭 Nginx 版本号
    client_max_body_size 614400m; #上传文件大小限制

    # 反向代理
    proxy_buffering off;
    proxy_max_temp_file_size 614400m; #反向代理上传文件大小限制
    proxy_set_header        X-Real_IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        Host            $http_host;
    proxy_pass_header       Set-Cookie;
    proxy_pass_header       X-CSRF-TOKEN;
    proxy_set_header Upgrade $http_upgrade; #连接升级为 WebSocket
    proxy_set_header Connection $connection_upgrade; #连接 map
    proxy_connect_timeout   60s; #代理超时
    proxy_read_timeout      60s; #读取超时
    proxy_send_timeout      60s; #请求改善超时
    #proxy_redirect         off;

    fastcgi_connect_timeout 600; #指定连接到后端 FastCGI 的超时时间
    fastcgi_send_timeout 600; #向 FastCGI 传送请求的超时时间
    fastcgi_read_timeout 600; #指定接收 FastCGI 应答的超时时间
    fastcgi_buffer_size 64k; #指定读取 FastCGI 应答第一部分需要用多大的缓冲区
    fastcgi_buffers 4 64k; #指定本地需要用多少和多大的缓冲区来缓冲 FastCGI 的应答请求
    fastcgi_busy_buffers_size 128k; #建议设置为 fastcgi_buffers 的两倍
    fastcgi_temp_file_write_size 128k; #在写入 fastcgi_temp_path 时将用多大的数据块,默认值是 fastcgi_buffers 的两倍
    fastcgi_temp_path /tmp; #缓存临时目录
    fastcgi_intercept_errors on; #指定是否传递 4xx 和 5xx 错误信息到客户端
    fastcgi_cache_path /tmp/blog levels=1:2 keys_zone=cache_fastcgi:300m inactive=60m max_size=3584m; #fastcgi_cache 缓存目录
    fastcgi_cache cache_fastcgi; #开启 FastCGI 缓存并为其指定一个名称
    fastcgi_cache_valid 200 302 1h; #用来指定应答代码的缓存时间
    fastcgi_cache_valid 301 1d; #将 301 应答缓存一天
    fastcgi_cache_valid any 1m; #将其他应答缓存为1分钟
    fastcgi_cache_min_uses 1; #设置经过多少次请求的相同 URL 将被缓存
    fastcgi_cache_key http://$host$request_uri; #设置 web 缓存的 Key 值
    #fastcgi_pass; #指定 FastCGI 服务器监听端口与地址

    gzip  on; #开启压缩
    gzip_disable "msie6";
    gzip_min_length 1k; #允许压缩的页面最小字节数
    gzip_buffers 32 4k; #压缩缓冲区大小
    gzip_http_version 1.1; #压缩版本
    gzip_comp_level 9; #压缩比例(1-9)
    gzip_proxied any; #对代理启用压缩
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_vary on; #varyheader 支持

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    include /etc/nginx/conf.d/*.conf;
}

我的 conf.d 里面本站服务器配置

server {    
    listen 443 ssl;
    #listen [::]:443 ssl ipv6only=on;
    server_name  sixdian.com;
    root         /home/site/sixdian.com;
    ssl_certificate /etc/letsencrypt/live/sixdian.com-0001/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sixdian.com-0001/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

    # expires 缓存调优
    location ~* \.(ico|jpeg|gif|png|bmp|swf|flv)$ {
        expires 30d;
        #log_not_found off;
        access_log off;
        }

        location ~* \.(js|css)$ {
        expires 1d;
        log_not_found off;
        access_log off;
        }

        # 防盗链(未成功)
        #location ~*^.+\.(jpg|gif|png|swf|flv|wma|wmv|asf|mp3|mmf|zip|rar)$ {
        #valid_referers sixdian.com;
        #if($invalid_referer) {
        #return 404;
        #break;
        #}
        #access_log off;
        #}
}

server {
    listen 80;
    #listen [::]:80 ipv6only=on;
    server_name  sixdian.com;
        return 301 https://sixdian.com$request_uri;
}

server {
    listen 80;
    server_name  www.sixdian.com;
        return 301 https://sixdian.com$request_uri;
}

我 conf.d 里面 Nextcloud 服务器配置

server {
    listen 443 ssl;
    server_name  drive.sixdian.com;
    ssl_certificate /etc/letsencrypt/live/sixdian.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sixdian.com/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

    location / {
    proxy_pass http://s1.sixdian.com:****;
    }

    location = /.well-known/carddav {
        return 301 $scheme://drive.sixdian.com/remote.php/dav/files/****/;
    }

    location = /.well-known/caldav {
        return 301 $scheme://drive.sixdian.com/remote.php/dav/files/****/;
    }
}

server {
    listen 80;
    server_name  drive.sixdian.com;
        return   301 https://drive.sixdian.com$request_uri;

    location = /.well-known/carddav {
        return   301 $scheme://drive.sixdian.com/remote.php/dav/files/****/;
    }

    location = /.well-known/caldav {
        return   301 $scheme://drive.sixdian.com/remote.php/dav/files/****/;
    }
}

我的 NextCloud 服务器 config.php 配置文件

位置在:/var/snap/nextcloud/current/nextcloud/config/config.php

<?php
$CONFIG = array (
  'trusted_proxies' =>
  array (
    0 => '******',
  ),
  'overwriteprotocol' => 'https',
  'default_phone_region' => 'CN',
  'apps_paths' =>
  array (
    0 =>
    array (
      'path' => '/snap/nextcloud/current/htdocs/apps',
      'url' => '/apps',
      'writable' => false,
    ),
    1 =>
    array (
      'path' => '/var/snap/nextcloud/current/nextcloud/extra-apps',
      'url' => '/extra-apps',
      'writable' => true,
    ),
  ),
  'supportedDatabases' =>
  array (
    0 => 'mysql',
  ),
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'memcache.local' => '\\OC\\Memcache\\Redis',
  'redis' =>
  array (
    'host' => '/tmp/sockets/redis.sock',
    'port' => 0,
  ),
  'log_type' => 'file',
  'logfile' => '/var/snap/nextcloud/current/logs/nextcloud.log',
  'logfilemode' => 416,
  'instanceid' => '******',
  'passwordsalt' => '********',
  'secret' => '******',
  'trusted_domains' =>
  array (
    0 => 'drive.sixdian.com',
  ),
  'datadirectory' => '/home/nextcloud',
  'dbtype' => 'mysql',
  'version' => '23.0.3.2',
  'overwrite.cli.url' => 'https://drive.sixdian.com',
  'dbname' => 'nextcloud',
  'dbhost' => 'localhost:/tmp/sockets/mysql.sock',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'mysql.utf8mb4' => true,
  'dbuser' => 'nextcloud',
  'dbpassword' => '********',
  'installed' => true,
  'mail_smtpmode' => 'smtp',
  'mail_smtpsecure' => 'ssl',
  'mail_sendmailmode' => 'smtp',
  'mail_from_address' => 'admin',
  'mail_domain' => 'sixdian.com',
  'mail_smtpauthtype' => 'LOGIN',
  'mail_smtpauth' => 1,
  'mail_smtphost' => 'smtp.exmail.qq.com',
  'mail_smtpport' => '465',
  'mail_smtpname' => 'admin@sixdian.com',
  'mail_smtppassword' => '********',
  'maintenance' => false,
  'loglevel' => 2,
  'overwritehost' => 'drive.sixdian.com',
);