盒子
盒子
文章目录
  1. 0.前言
  2. 1.软件版本
  3. 2.删除Let’s Encrypt的Https证书
  4. 3.配置Apache虚拟主机
  5. 4.配置Nginx
    1. 4.1.设置反向代理端口
    2. 4.2.增加 proxy_set_header 配置
    3. 4.3.给所有代理网站加上Https证书
    4. 4.4.无法获得二级域名/三级域名的情况下,如何仅代理二级目录问题
    5. 4.5.19年3月底的再次更新部分
  6. 5.总结

Nginx反向代理Apache,并解决Laravel在反向代理情况下的HTTPS加密问题

0.前言

  由于想利用nginx学习均衡负载和反向代理的相关方法,未来项目中极有可能应用其中,所以决定先利用自己的服务器入手。六月份还无耐心地试过一次,没有成功,这次决心重来。由于之前Apache上的网站均使用Let’s Encrypt的证书加密过,所以直接通过nginx反向代理无法访问Https的服务,所以坑开始了。
此次,希望可以利用Nginx做前端代理,Apache做后端服务器,但也不想改变现有的网站结构,同时未来还可能会在服务器上搭建一个轻量级的git服务。

1.软件版本

  • Ubuntu 版本:Ubuntu 16.04.4 LTS
  • Apache 版本:Apache/2.4.18 (Ubuntu)
  • Nginx 版本:nginx/1.10.3 (Ubuntu)
  • Laravel 版本:5.5.X

    2.删除Let’s Encrypt的Https证书

    由于之前Apache上的网站均使用Let’s Encrypt的证书加密过,所以这里需要删除所有的Https证书和相关文件。我选择比较愚蠢的办法,删除了位于
    1
    2
    /etc/apche2/sites-available
    /etc/apache2/sites-enabled

下,所有后缀名为:*-ssl.conf的文件,并删除/etc目录下 letsencrypt文件夹下的所有文件,最后重启apache服务器。

3.配置Apache虚拟主机

  首先,改变Apache的监听端口,修改ports.conf文件,如下:

1
2
3
#Listen 80 #注释掉原有的80端口
Listen 8080 #给虚拟主机配置端口号
Listen 8081 #如有多个虚拟主机,需要配置多个端口

  其次,更改每一个虚拟主机的配置文件,如下:

1
2
3
4
5
\#<VirtualHost \*:80> #注释掉原有的80端口
<VirtualHost 127.0.0.1:8080> #只监听本地发送到8080端口的请求
ServerName test.jiacyer.com
DocumentRoot /var/www/website_dir
</VirtualHost>

Apache配置完成。

4.配置Nginx

4.1.设置反向代理端口

  修改Nginx根目录下的nginx.conf配置文件,在其 http 属性下添加:

1
2
3
4
5
6
7
server {
server_name test.jiacyer.com;
location / {
proxy_pass http://127.0.0.1:8080;
}
}

  • 属性 proxy_pass 的IP末尾是否带‘/’,可以参考Nginx之前端代理Laravel的解释。
    此时,已经可以访问静态网页的网站,并能够正确加载所有js、css样式等。

      但基于Laravel框架构建的动态网页,js、css定位错误,动态生成类似 http://127.0.0.1:8080/js/xxx.js 的资源定位。我们访问的是80端口,但这里css文件下载路径,确变成了8080端口,无法正常加载我们所需要的文件。借用别人文章里调试的截图,如下:

4.2.增加 proxy_set_header 配置

  这里需要增加一个代理配置属性,增加proxy_set_header Host $host:$server_port;为了保证Laravel能正确获取客户端IP,还可以增加proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;属性。

1
2
3
4
5
6
7
8
9
10
server {
server_name test.jiacyer.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

  笔者并没有仔细分析以上两个属性的用法,详细可参看HTTP 请求头中的 X-Forwarded-For
  此时,在没有进行Https加密的情况下,基于Laravel框架的网站已经能够正常访问了。

4.3.给所有代理网站加上Https证书

  这里,我依旧选择的是免费的Let’s Encrypt的证书加密,按照官方教程,顺利安装好证书,并强制使用Https访问网站。然而,我们优秀的Laravel框架又翻车了。
  经过Nginx反向代理,可以通过Https访问对应的html文件,但是所有的js、css资源文件却都是http的,Chrome、Edge等浏览器都会以不安全为由,拦劫这样的资源加载,我们又看到了熟悉的纯html网页,通过浏览器的检查功能,我们可以看到如下文字:

1
Mixed Content: The page at 'https://test.jiacyer.com/' was loaded over HTTPS, but requested an insecure stylesheet 'http://test.jiacyer.com/css/animate.css'. This request has been blocked; the content must be served over HTTPS.



  这个锅,Nginx不背!原因是,如果检测到传入连接是安全的,Laravel 5.x将通过route()帮助程序生成安全URL。如果应用程序隐藏在负载均衡器或代理(例如Cloudflare)后面,通常会发生问题,因为应用服务器和负载均衡器/代理之间的连接可能不安全。根据Laravel 5.5的官方文档可以找到解决方式。另外在“题 Laravel从路由生成安全的https URL”问题下,也有相当多的讨论可供参考。
  更改App\Http\Middleware\TrustProxies文件中的$proxies参数。

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
28
29
30
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array
*/
protected $proxies = [
'192.168.1.1',
'192.168.1.2',
];
/**
* The current proxy header mappings.
*
* @var array
*/
protected $headers = [
Request::HEADER_FORWARDED => 'FORWARDED',
Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
];
}

  同样,可以选择信任所有代理者。如果您使用的是Amazon AWS或其他“云”负载均衡器提供商,您可能不知道实际余额的IP地址。在这种情况下,您可以使用信任所有代理:**

1
2
3
4
5
6
/**
* The trusted proxies for this application.
*
* @var array
*/
protected $proxies = '**';

  最后,在nginx的nginx.conf配置文件对应代理的location字段中添加proxy_set_header X-Forwarded-Proto $scheme;,并在server属性末尾,添加如下代码:

1
2
3
if ($scheme != "https") {
return 301 https://$host$request_uri;
}

  至此,终于能够正常的访问基于Laravel框架的Htps加密网站。

4.4.无法获得二级域名/三级域名的情况下,如何仅代理二级目录问题

  在知乎上看到过一个相关问题,“域名的二级目录nginx反向代理到laravel项目,如何解决路由问题?”,在 勤劳一沙鸥 的Nginx之前端代理Laravel里有相应的解决方案,不再赘述。

4.5.19年3月底的再次更新部分

  之前是自己的主机下有多个虚拟主机网站,这次自己也遇到了4.4的情况,无法申请别人域名的情况下,如何完成二级目录的代理,同时我也是前后端分离项目。前端使用Vue.js,服务器直接使用Nginx,后端使用Spring,服务器使用Tomcat,如何实现不跨域的前后端通信,这就需要用到Nginx的反向代理了。我的配置http属性下添加了如下代码:

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
28
29
30
upstream api_domain {
server t1:8080;
}
server {
root /usr/share/nginx/html;
location ^~ /api/ {
proxy_pass http://api_domain/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ^~ /test/ {
proxy_pass http://api_domain/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
try_files $uri $uri/ @router;
index index.html index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
}

  其中,^~ /api/是指以/api/开头的url统统转发到后端的http://api_domain/api/链接下,而^~ /test/是指以/test/开头的url统统转发到后端的http://api_domain/链接下。剩下的所有链接,直接转跳至index.html,因为 Vue.js 帮我们完成了所有的路由操作,如果不转跳就会出现404错误。

5.总结

  找遍google里的相关文章,基本上没有很好完整解决Laravel框架从代理到https加密的问题,所以希望写下这篇文章,能够帮助到一些朋友。

参考贴来源:Nginx之前端代理Laravel
题 Laravel从路由生成安全的https URL

转载说明

转载请注明出处,无偿提供。

支持一下
感谢大佬们的支持