V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
famanoder
V2EX  ›  Node.js

做个前端,来点 Nginx

  •  
  •   famanoder · 2017-09-03 09:00:44 +08:00 · 8619 次点击
    这是一个创建于 2398 天前的主题,其中的信息可能已经有所发展或是发生改变。

    自从 Nodejs 火了,前端能做的事、要做的事越来越多了;同时对前端的要求也就越来越高,如果现在还只是停留在浏览器端写页面做交互,估计很难找到(更好的)工作了,Node 中间层、Node 微服务、网关这些可以和业务分离的地方以后可能都是前端的事了; Nodejs 是把锋利的瑞士军刀,但你也不要想多了;合理的选型,各司其职,职尽其能,才能发挥各自最大的作用;毕竟一切从实际出发,实事求是,理论联系实际才是最佳的方法论;比如 Nodejs 可以做反向代理( http-proxy ),可以很快的搭建静态资源站,但这些并不是 Nodejs 最擅长的,这些交给 Nginx 显然是个更好的选择,既可以把这些事做更好,还给 Nodejs 服务减压了!

    一、快速拾起 Nginx

    Nginx 是一个高性能的 Web 和反向代理服务器,稳定、强大、系统资源占用低,这些就不说了;

    在 nginx.conf 这个配置文件里,一个 server {}块可以对应一个站点的服务,每个 server {}块里可以配置多个 location {}块来对站点进行路由级别的控制,既可以通过 proxy_pass target 设置反向代理的 server,也可以直接通过 root dir 来访问目录下的静态文件; server_name 设置访问的域,多个用空格隔开,或者用通配符和正则; location 后面可以是正则以及 nginx 提供的丰富的匹配符和变量;记住大括号前面的空格不能省,每行结束语句的分号不能省;

    a. 比如用 Nodejs 启动了一个站点监听 3000 端口,用 a.famanoder.cn 来访问

    server {
        listen 80;
        server_name a.famanoder.cn;
    	
    	location / {
            proxy_pass http://localhost:3000;
        }
    }
    

    b. 比如把所有的静态资源放到了 dist 目录,用 cdn.famanoder.cn 来访问

    server {
        listen 80;
        server_name cdn.famanoder.cn;
    	
    	location / {
            index index.html;
            root D:\sources\dist;
        }
    }
    

    c. 用 vue 做的一个移动端的项目,用 m.famanoder.cn 来访问,所有数据接口由 famanoder.cn 提供;

    server {
        listen 80;
        server_name m.famanoder.cn;
    	
    	location /api {
    		# 代理 api,以免跨域
            proxy_pass https://famanoder.cn;
        }
        location / {
            index index.html;
            root D:/vue/dist;
        }
    }
    

    以上只是最简单的站点和反向代理设置,通过匹配符和正则可以做更多的控制; 二、常用匹配符和变量

    =   等于,严格匹配
    !=  不等于
    ~   区分大小写匹配
    !~  区分大小写不匹配
    ~*  不区分大小写匹配
    !~* 不区分大小写不匹配
    *   任意字符
    -f 和!-f 判断是否存在文件
    -d 和!-d 判断是否存在目录
    -e 和!-e 判断是否存在文件或目录
    -x 和!-x 判断文件是否可执行
    

    对于 location,匹配的优先级为: (location =) > (location 完整路径) > (location ^~ 路径) > (location ~,~* 正则顺序) > (location 部分起始路径) > (/)

    Nginx 里有 if 指令,但是没有 else 指令和&&判断,但可以通过 set 变通的实现: 比如限制 GET 请求参数中的 SQL 关键词:

    set $invalidQuery 1;
    if( $request_method = GET ){
    	set $invalidQuery '${invalidQuery}1';
    } 
    if( $query_string ~* "select|union|exec" ){
    	set $invalidQuery '${invalidQuery}1';
    } 
    if( $invalidQuery = '111' ){
    	return 403;
    }
    

    常用变量:

    $args: 请求行中的参数,同$query_string。等于 js 中的 location.search.slice(1)。
    $content_type: 请求头中的 Content-Type 字段。
    $document_root: 当前请求在 root 指令中指定的值。
    $host: 请求主机头字段,否则为服务器名称。等于 js 中的 location.host。
    $http_user_agent: 客户端 agent 信息。等于 js 中的 navigator.userAgent。
    $http_cookie: 客户端 cookie 信息。等于 js 中的 document.cookie。
    $limit_rate: 这个变量可以限制连接速率。
    $request_method: 客户端请求的动作,通常为 GET 或 POST。
    $remote_addr: 客户端的 IP 地址。
    $remote_port: 客户端的端口。等于 js 中的 location.port。
    $http_referer:网页来源。等于 js 中的 document.referer。
    $remote_user: 已经经过 Auth Basic Module 验证的用户名。
    $request_filename: 当前请求的文件路径,由 root 或 alias 指令与 URI 请求生成。
    $scheme:HTTP 方法(如 http,https )。等于 js 中的 location.protocol。
    $server_protocol: 请求使用的协议,通常是 HTTP/1.0 或 HTTP/1.1。
    $server_addr: 服务器地址,在完成一次系统调用后可以确定这个值。
    $server_name: 服务器名称。
    $server_port: 请求到达服务器的端口号。
    $request_uri: 包含请求参数的原始 URI,不包含主机名,等于 js 中的 location.pathname+location.search。
    $uri: 不带请求参数的当前 URI,$uri 不包含主机名,等于 js 中的 location.pathname。
    $document_uri: 与$uri 相同。
    

    三、常见设置:

    1、worker_processes 4 # 开启多进程,一般为 cpu 核数,等于 Nodejs 中的 require('os').cpus().length

    2、error_log logs/error.log info; # log 文件的地址和级别( debug, info, notice, warn, error, crit )

    3、log_format 格式名称 具体格式 # 定义日志内容的格式可以包含$remote_addr $status $http_user_agent 等参数

    4、开启 gzip

    gzip  on;
    gzip_proxied any;  
    gzip_min_length  1024;
    gzip_buffers     4 8k;
    gzip_types       text/css application/javascript application/atom+xml application/rss+xml text/plain image/svg+xml application/json text/javascript; 
    

    默认的配置文件中只有 gzip on 作用不大,需要自行配置后续 gzip 字段;给所有需要开启 gzip 的资源添加 mimeType,图片不需要 gzip (没有明显效果,体积还可能增大),白白损耗性能;

    四、解决前端跨域问题

    在前后端分离的时候,前后端搭建了两套环境,前端请求数据的时候会跨域,一般是用 Nodejs 做中转,比如使用 http-proxy 和 request 模块,或者在 webpack 的 dev-server 里配置 proxy,浏览器兼容性比较理想的情况下还可以直接设置 CORS ;这样对于打包上线也不需要做太多改动;当然有时候还需要 jsonp ;

    a. 设置 CORS

    server {
        listen 80;
        server_name cdn.famanoder.cn;
    
        location / {
        	add_header Access-Control-Allow-Origin *;  
            add_header Access-Control-Allow-Credentials true;  
            add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
            index index.html;
            root D:/sources/dist;
        }
    }
    

    如果不想设置星号的话,这个变通的做法貌似更灵活,还可以通过跨域反过来限制某些资源的是否可访问

    if ($http_referer ~* 'famanoder.cn') {  
        add_header Access-Control-Allow-Origin *;  
        add_header Access-Control-Allow-Credentials true;  
        add_header Access-Control-Allow-Methods GET,POST,OPTIONS;  
    }
    

    b. 做 api 中转

    server {
        listen 80;
        server_name m.famanoder.cn;
    
        location /api {
        	proxy_pass https://localhost:3000;
        }
    }
    

    五、从 http 切换到 https

    对于一般的散户来说,Letsencrypt 是个不错的选择,可以免费为多个域名提供一套证书(散户福利! Nodejs 多站点切换 Htpps 协议);可以新建一个站点专门为申请证书服务,以免以后重新申请时重启应用或再次搭建;对于切换到 https,只需在 80 端口上直接对指定域名做 301 跳转到 https 对应地址,server 块内做很小改动即可:

    server {
        listen 80;
        server_name *.famanoder.com famanoder.com;
    
        location / {
        	# 迁移到 https
            return 301 https://$host$request_uri;
        }
    
        location ~* 'acme-challenge' {
    		# 2333 端口留做以后申请证书用
            proxy_pass http://localhost:2333;
        }
    }
    server {
    	# 监听 443 端口,开启 ssl
        listen 443 ssl;
        server_name cdn.famanoder.cn;
        # 把申请到的证书加进来
        ssl_certificate      D:/crt.pem;
    	ssl_certificate_key  D:/key.pem;
    	
    	location / {
            index index.html;
            root D:\sources\dist;
        }
    }
    

    六、Nginx 真的超级强大,而且配置起来并不繁琐,常常简单的设置就可以达到非常强大的功能,比如做安全访问限制、负载均衡等;

    如果你很喜欢一个东西,那么是否应该拿它来做更多有意义的事情!如果你很喜欢 JS,那么是否应该拿它来做更多有意义的事情!那么好的,听说新版 Nginx 开始支持 js 语法了!如果你已在路上,就勇敢的向前吧!

    原文来自:花满楼( https://famanoder.com

    24 条回复    2017-10-24 11:06:48 +08:00
    a1044634486
        1
    a1044634486  
       2017-09-03 10:42:10 +08:00
    我感觉楼主挺厉害的,会前端还会搞 nginx。。
    shengyu
        2
    shengyu  
       2017-09-03 10:51:38 +08:00
    觉得 Caddy 更简单点
    ychongsaytc
        3
    ychongsaytc  
       2017-09-03 11:32:51 +08:00
    `server_name *.famanoder.com famanoder.com;`

    可以缩写为

    `server_name .famanoder.com;`

    参考 http://nginx.org/en/docs/http/server_names.html
    assad
        4
    assad  
       2017-09-03 11:34:00 +08:00 via Android
    好好写你的前端,非要凑活后端的事
    deleteDB
        5
    deleteDB  
       2017-09-03 12:13:30 +08:00
    战略马克一下
    v1024
        6
    v1024  
       2017-09-03 12:48:17 +08:00 via iPhone
    你说这些是运维的事,这样就是全栈范畴,只不过是从前端转过来的而已。
    webster
        7
    webster  
       2017-09-03 12:52:29 +08:00
    nginx 很简单吧 作为一个前端从 0.8.x 的时候就开始会了 只是现在配置越来越多没时间看文档
    xfspace
        8
    xfspace  
       2017-09-03 12:55:09 +08:00 via Android
    把 SSL 证书链补全下?



    描述:</b>花满楼的小站,一个小前端业余的自娱自乐;自打步入前端,从最初的 html 到 HTML5,从最初的 css 到 CSS3,从最初的 ES3 到 ES6,从最初的 JavaScript 到 Nodejs,一路走来,浪迹前端&nbsp;<span id="y"></span>&nbsp;载有余,说实话,除了颜值,没什么拿的出手的了,仅以此记录年轻的追逐和前进的脚步!
    hcymk2
        9
    hcymk2  
       2017-09-03 13:00:03 +08:00
    jswh
        10
    jswh  
       2017-09-03 13:53:05 +08:00
    node 是一把瑞士军刀。但是,瑞士军刀的问题,就是,太小了......。砍树还是要用斧子,打猎还是要用猎枪,甚至杀人还是要用匕首。
    famanoder
        11
    famanoder  
    OP
       2017-09-03 17:13:18 +08:00
    @ychongsaytc 是的
    famanoder
        12
    famanoder  
    OP
       2017-09-03 17:13:51 +08:00
    @assad O(∩_∩)O 哈哈~
    famanoder
        13
    famanoder  
    OP
       2017-09-03 17:15:07 +08:00
    @xfspace 就是一个文件地址,我故意不补全的
    famanoder
        14
    famanoder  
    OP
       2017-09-03 17:15:34 +08:00
    @jswh 对,其实我就是这个意思
    zuolan
        15
    zuolan  
       2017-09-03 17:28:16 +08:00
    啊,原来我有个误区,我一直觉得 Nginx 这种 Web Server 是前端的必备技能,毕竟前端性能优化不能少了 Web Server 的调优,但是看到楼上的回复,emmmm....是我理解错了吗?
    famanoder
        16
    famanoder  
    OP
       2017-09-03 17:41:29 +08:00
    @zuolan 如果工作专职做前端的话,几乎接触不到 nginx 吧?除了自己本地搭建开发环境调试用
    dikT
        17
    dikT  
       2017-09-04 08:53:53 +08:00
    windows server? doge
    zhailei2011
        18
    zhailei2011  
       2017-09-04 09:33:04 +08:00
    Mark
    del1214
        19
    del1214  
       2017-09-04 10:50:03 +08:00
    mark
    sm0king
        20
    sm0king  
       2017-09-04 11:45:37 +08:00
    @assad
    @famanoder

    好可怜,我们公司后端 nginx 貌似不熟悉,每次都是我将 nginx 配置好给他们。每次 nginx 出问题了也是我处理。
    dxcqcv
        21
    dxcqcv  
       2017-09-12 12:55:05 +08:00
    问个问题,

    环境公司内网 redhat 6.0
    应用 node koa,跑在 ip:3000 端口
    想要实现,不加端口号访问,比如直接 ip 地址访问

    现在的问题,无法实现代理,输入 IP 只给我 nginx 的测试页面

    nginx 配置写在自建目录 sites-available/下,名字为 tutorial

    内容为

    ```
    server {
    listen 80;
    server_name 10.xx.xx.xxx;
    location / {
    proxy_pass 10.xx.xx.xxx:3000;
    }
    }
    ```

    快捷方式 ln 到 sites-enabled/目录下
    添加 include 到 nginx.conf 的 http 字段内

    ```
    include /etc/nginx/sites-enabled/*
    ```

    求 LZ 指点一下,哪里错了? nginx -v 1.0.15
    dxcqcv
        22
    dxcqcv  
       2017-09-12 13:38:28 +08:00
    我多方搜索,发现是不是 server_name 不能直接用 ip,一定要用域名?那么在公司内网的服务器中,只有 ip,是否还能用 nginx 来代理呢?
    haierspi
        23
    haierspi  
       2017-10-23 16:54:29 +08:00
    @sm0king 你们公司运维 真是可以滚蛋回家了...头一次听说 线上服务器 允许一个写前端的同事登陆到服务器上操作服务器... 天啊.. 这在我们这... 就是重大事故..
    sm0king
        24
    sm0king  
       2017-10-24 11:06:48 +08:00
    @haierspi 不是啊,我们不允许上去操作服务器的,我们会把我们对 nginx 的需求,给他们写好,发给他们,他们上线,然后出问题后,他们会给我们看日志,这个算是术业专攻吧,我们看日志可以更快更准确的查到问题所在。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1017 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 19:31 · PVG 03:31 · LAX 12:31 · JFK 15:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.