浅析 Laravel 官方文档推荐的 Nginx 配置

Laravel 5.8 文档 为准,浅析 Nginx 配置。可作为 轻松部署 Laravel 应用 的拓展阅读。

方便起见,我在注释中使用 [] 包裹引用配置中的值。

server {
    # 监听 HTTP 协议默认的 [80] 端口。
    listen 80;
    # 绑定主机名 [example.com]。
    server_name example.com;
    # 服务器站点根目录 [/example.com/public]。
    root /example.com/public;

    # 添加几条有关安全的响应头;与 Google+ 的配置类似,详情参见文末。
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    # 站点默认页面;可指定多个,将顺序查找。
    # 例如,访问 http://example.com/ Nginx 将首先尝试「站点根目录/index.html」是否存在,不存在则继续尝试「站点根目录/index.htm」,以此类推...
    index index.html index.htm index.php;

    # 指定字符集为 UTF-8
    charset utf-8;

    # Laravel 默认重写规则;删除将导致 Laravel 路由失效且 Nginx 响应 404。
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # 关闭 [/favicon.ico] 和 [/robots.txt] 的访问日志。
    # 并且即使它们不存在,也不写入错误日志。
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    # 将 [404] 错误交给 [/index.php] 处理,表示由 Laravel 渲染美观的错误页面。
    error_page 404 /index.php;

    # URI 符合正则表达式 [\.php$] 的请求将进入此段配置
    location ~ \.php$ {
        # 配置 FastCGI 服务地址,可以为 IP:端口,也可以为 Unix socket。
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        # 配置 FastCGI 的主页为 index.php。
        fastcgi_index index.php;
        # 配置 FastCGI 参数 SCRIPT_FILENAME 为 $realpath_root$fastcgi_script_name。
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        # 引用更多默认的 FastCGI 参数。
        include fastcgi_params;
    }
    # 通俗地说,以上配置将所有 URI 以 .php 结尾的请求,全部交给 PHP-FPM 处理。

    # 除符合正则表达式 [/\.(?!well-known).*] 之外的 URI,全部拒绝访问
    # 也就是说,拒绝公开以 [.] 开头的目录,[.well-known] 除外
    location ~ /\.(?!well-known).* {
        deny all;
    }
}

关于 X-Frame-OptionsX-XSS-ProtectionX-Content-Type-Options 可参考 这篇文章,自认为作者讲得还不错,通俗易懂并且是中文。

关于 .well-known 目录的详细解释,可参考 这篇问答(英文)。

本作品采用《CC 协议》,转载必须注明作者和本文链接
Former WinForm and PHP engineer. Now prefer Golang and Rust, and mainly working on DevSecOps and Kubernetes.
附言 1  ·  4年前

感谢 @soli,请大家注意这条留言:博客:浅析 Laravel 官方文档推荐的 Nginx 配置

本帖由系统于 5年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 34
soli

非常不错的文章。我也从中选了几条我没有但非常有用的配置添加到我的配置中了。

值得注意的是,文中给的关于 X-Frame-Options那篇文章中,第一条 Strict-Transport-Security,使用的时候要谨慎一些。即使用,也不要把 max-age 设置的太大。否则,万一配置有误或者需要修改配置,那你将无法让那些已经保存过 HSTS 的用户使用新的配置。

举个例子,比如你把 max-age 设置成一年。但半年的时候你的 HTTPS 证书到期了。由于某种原因你不想续费 HTTPS 证书了。所以,你修改配置文件,删除 Strict-Transport-Security 等配置,重新启用 HTTP 。
但那些已经保存了 HSTS 的用户在接下来的半年内,还是会直接访问你的 https 站点,并得到证书过期的红色警告,浏览器拒绝打开你的网站。即使用户手动输入 http 都不行。

4年前 评论

@overfalse

try_files 指令可让 Nginx 按照特定的顺序查找文件。按照以下配置为例:

try_files $uri $uri/ /index.php?$query_string;

假设你正在访问 /api/status?query=example

  1. Nginx 会首先尝试 $uri 是否存在(这是个 Nginx 内置变量,表示请求 URI),也就是 /网站根目录/api/status
  2. 如果 $uri 不存在,则尝试 $uri/,也就是 /网站根目录/api/status/
  3. 如果 $uri/ 也不存在,则最终尝试 /index.php?$query_string$query_string 也是 Nginx 的内置变量,表示 URI 内的查询字符串),也就是 /网站根目录/index.php?query=example
  4. 按照 Laravel 的目录结构,/网站根目录/index.php 是必定存在的,但如果该文件确实不存在,Nginx 将会返回 404。

需要注意的是,严格地说,/api/status/api/status/ 这两个 URI 并不指向同一个资源。同理,https://www.google.comhttps://www.google.com/ 也不等效。有兴趣可 Google trailing slash in uri 之类的关键词获取更多细节。

PS,ID 很不错😂。下次我改个 overnull

5年前 评论
oyghan 4年前

@晨读秀 正如 @molaifeng 所说,后者相比前者无需经过 TCP/IP。理想状态下,只有应用层的内存数据拷贝,所以相对来说资源占用小,但速度提升基本可以忽略(太小)。你可以 Google tcp/ip socket vs unix socket 来找到更多信息。

另外,Unix Socket 由于并不属于「网络通信协议」,而是一种「进程通讯机制」,因此有个不太常见的局限:无法跨机器使用。即使通过 NFS 等协议共享 Unix Socket 文件也无效。

5年前 评论

虽然这是一篇讲解 Nginx 配置的文章,但是却让我突然弄清楚了正则表达式中的 零宽断言,,,以前一直都看的云里雾里,就没管了,反正也很少用,,,

5年前 评论

@largezhou 哈哈,是的,最后的 /\.(?!well-known).* 便是断言,即仅匹配但不捕获,可以理解为一个判断条件。断言有很多种,这一种称作 否定前瞻断言(这是个不标准、不统一的称呼),也叫 Negative Lookahead(比较通用的叫法)。

5年前 评论
晨读秀

您好,请教一下

# 配置 FastCGI 服务地址,可以为 IP:端口,也可以为 Unix socket。
  fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;

这里讲到的 IP:端口 和 Unix socket 的使用有什么区别吗? 或者是利弊

5年前 评论

@晨读秀 前者走的是 TCP/IP 协议栈,后者走的是 IPC,前者稳定些,具体可参考 PHP-FPM 与 Nginx 的通信机制总结

5年前 评论

@晨读秀 正如 @molaifeng 所说,后者相比前者无需经过 TCP/IP。理想状态下,只有应用层的内存数据拷贝,所以相对来说资源占用小,但速度提升基本可以忽略(太小)。你可以 Google tcp/ip socket vs unix socket 来找到更多信息。

另外,Unix Socket 由于并不属于「网络通信协议」,而是一种「进程通讯机制」,因此有个不太常见的局限:无法跨机器使用。即使通过 NFS 等协议共享 Unix Socket 文件也无效。

5年前 评论
晨读秀

@Wi1dcard @molaifeng

多谢解惑! :pray:

5年前 评论
晨读秀

您好,希望您有时间可以添加上 HTTPS 的配置。 :pray: @Wi1dcard

5年前 评论

@晨读秀 好的,没问题;已经列入计划中。

5年前 评论
晨读秀

@Wi1dcard 多谢。 :pray:

5年前 评论
lmaster

学习零宽断言.well-known
关于 tcp/ip socket vs unix socket 的比较,大多数都提的是 unix socket 性能好,但高并发有瓶颈,为什么那?

5年前 评论

@lmaster 关于 Unix Socket 的性能瓶颈还真没有听说过,给个链接?

5年前 评论
lmaster

@Wi1dcard PHP-FPM 与 Nginx 的通信机制总结 ,百度上类似PHP-FPM 与 Nginx 的通信标题的博文里面多多少少都说unix socket高并发有瓶颈、不稳定,我也是对这个感觉很奇怪

5年前 评论

@lmaster 并不是 Unix Socket 不稳定,我猜是没有做好内核参数调优;可供参考的配置很多,例如:https://gist.github.com/voluntas/bc54c60aa...

百度的文章太浅显,容易误人子弟;搜娱乐还行,技术向还是用谷歌吧。

5年前 评论

try_files $uri $uri/ /index.php?$query_string; 这段啥意思呢?

5年前 评论
沉默 4年前

@overfalse

try_files 指令可让 Nginx 按照特定的顺序查找文件。按照以下配置为例:

try_files $uri $uri/ /index.php?$query_string;

假设你正在访问 /api/status?query=example

  1. Nginx 会首先尝试 $uri 是否存在(这是个 Nginx 内置变量,表示请求 URI),也就是 /网站根目录/api/status
  2. 如果 $uri 不存在,则尝试 $uri/,也就是 /网站根目录/api/status/
  3. 如果 $uri/ 也不存在,则最终尝试 /index.php?$query_string$query_string 也是 Nginx 的内置变量,表示 URI 内的查询字符串),也就是 /网站根目录/index.php?query=example
  4. 按照 Laravel 的目录结构,/网站根目录/index.php 是必定存在的,但如果该文件确实不存在,Nginx 将会返回 404。

需要注意的是,严格地说,/api/status/api/status/ 这两个 URI 并不指向同一个资源。同理,https://www.google.comhttps://www.google.com/ 也不等效。有兴趣可 Google trailing slash in uri 之类的关键词获取更多细节。

PS,ID 很不错😂。下次我改个 overnull

5年前 评论
oyghan 4年前

讲的非常好,特别是 Wi1dcard 回答问题真的是我见过最仔细的! 感谢

4年前 评论
soli

非常不错的文章。我也从中选了几条我没有但非常有用的配置添加到我的配置中了。

值得注意的是,文中给的关于 X-Frame-Options那篇文章中,第一条 Strict-Transport-Security,使用的时候要谨慎一些。即使用,也不要把 max-age 设置的太大。否则,万一配置有误或者需要修改配置,那你将无法让那些已经保存过 HSTS 的用户使用新的配置。

举个例子,比如你把 max-age 设置成一年。但半年的时候你的 HTTPS 证书到期了。由于某种原因你不想续费 HTTPS 证书了。所以,你修改配置文件,删除 Strict-Transport-Security 等配置,重新启用 HTTP 。
但那些已经保存了 HSTS 的用户在接下来的半年内,还是会直接访问你的 https 站点,并得到证书过期的红色警告,浏览器拒绝打开你的网站。即使用户手动输入 http 都不行。

4年前 评论
聪聆

配置信息不错,但是现在基本上都是https了,可以借鉴其中几条

4年前 评论

@聪聆 HTTPS 和官方推荐的配置项不冲突。

4年前 评论

@晨读秀 两种配置分别对应着nginx和php-fpm之间的两种通信方式。

1.unix socket通信

fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;

2. tcp socket通信

fastcgi_pass   127.0.0.1:9000;

3.区别

由于 Unix socket 不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。所以其效率比 tcp socket 的方式要高,可减少不必要的 tcp 开销。不过,unix socket 高并发时不稳定,连接数爆发时,会产生大量的长时缓存,在没有面向连接协议的支撑下,大数据包可能会直接出错不返回异常。而 tcp 这样的面向连接的协议,可以更好的保证通信的正确性和完整性。

4年前 评论
阿麦

支持支持

4年前 评论

你好,读了你的文章,受益匪浅。 当前遇到了一个php-fpm的问题。php 版本 7.3.9 php-fpm版本 7.1.26,请求域名502。希望给写指导

file

4年前 评论

@Benny 502 Bad Gateway 一般是 Nginx 无法与上游通讯,在 Nginx 和 PHP-FPM 的场景下,上游就是 FPM 进程。所以建议你:

  1. 检查一下 PHP-FPM 的配置(/usr/local/etc/php/7.3/php-fpm.conf)看看监听的端口或 Unix Socket 路径是否正确。
  2. 确认 FPM 进程正在运行(ps aux | grep fpm),且已经监听对应端口(ls <UNIX_SOCKET_PATH> / telnet localhost <PORT>)。
  3. 确认 Nginx 配置的 FPM 端口或 Unix Socket 路径正确。
4年前 评论

file

file
现在遇到的问题是,访问域名502。不知道是不是FPM哪里配置出错了 。

4年前 评论

@Benny 如果你在用 Valet 的话,建议你直接全部卸载重装。另外,ping 并不能说明问题。

4年前 评论

执行这个命令 tail -n 10 /usr/local/var/log/php-fpm.log发现 log文件一直在写入日志
file

4年前 评论

@Benny
看起来有可能你安装了多个 PHP-FPM。
file

4年前 评论
Benny 4年前

偶然发现 当我关了v-p-n , 就能够访问了 。不清楚为什么 ; 开着v-p-n ,执行valet share 可以访问

4年前 评论

很可以,支持!

4年前 评论

很好的文章,之前还点赞过,就近收藏夹吃灰了。 最近又想着看下源码,看源码之前想说在了解下laravel的nginx配置,gogo一搜索,就看到这个文章。 内容真的不错,评论下讨论也行。

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
67
粉丝
590
喜欢
1235
收藏
1133
排名:13
访问:32.4 万
私信
所有博文
社区赞助商