HAProxy Proxying

了解如何使用 HAProxy 等代理应用程序来保护您的 Strapi 应用程序。

由于 Strapi 不直接处理 SSL,并且在“边缘”网络上托管 Node.js 服务并不是一个安全的解决方案,因此建议您使用某种代理应用程序,例如 Nginx、Apache、HAProxy、Traefik 或其他应用程序。以下文档提供了 HAProxy 的一些示例配置,当然这些配置可能并不适合所有环境,您可能需要调整它们以满足您的需求。

配置

以下示例充当“SSL 终止”代理,这意味着 HAProxy 仅接受 SSL 上的请求并代理到其他后端服务,例如 Strapi 或其他 Web 服务器。HAProxy 无法提供静态内容,因此它通常用于在故障转移或负载平衡情况下处理多服务器部署。以下示例基于同一服务器上存在的所有内容,但可以轻松调整以适应多服务器部署。

Strapi 服务器

为了充分利用代理 Strapi 应用程序,应配置 Strapi 以使其了解上游代理。与以下配置一样,有 3 个匹配的示例。可以在 服务器配置管理配置 文档中找到更多信息。

这些示例使用默认 API 前缀 /api。无需直接修改 Nginx 配置即可更改此前缀(请参阅 API 前缀 文档)。

如果在 ./config/admin.js./config/server.js 文件中更改了 url 键,则需要使用 yarn buildnpm run build 重建管理面板。

子域名 Strapi 配置

  • 示例域名:api.example.com
  • 示例管理员:api.example.com/admin
  • 示例 API:api.example.com/api
  • 示例上传文件(本地提供商):api.example.com/uploads
js
path: ./config/server.js
js
module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: 'https://api.example.com',
})

子文件夹统一 Strapi 配置

  • 示例域:example.com/test
  • 示例管理员:example.com/test/admin
  • 示例 API:example.com/test/api
  • 示例上传文件(本地提供商):example.com/test/uploads
js
path: ./config/server.js
js
module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: 'https://example.com/test',
})

HAProxy

以下示例要么将所有请求直接代理到 Strapi,要么在 Strapi 和其他后端 Web 服务器(如 Nginx、Apache 等)之间拆分请求。

以下是 2 个示例 HAProxy 配置:

  • 基于子域,例如 api.example.com
  • 基于子文件夹,API 和 Admin 都位于同一子文件夹中,例如 example.com/test/apiexample.com/test/admin
不支持子文件夹拆分

Strapi 不支持也不推荐使用子文件夹拆分(例如:https://example.com/dashboardhttps://example.com/api)。建议您使用子域名(例如:https://api.example.com)或子文件夹统一(例如:https://example.com/strapi/dashboardhttps://example.com/strapi/api)。

::card{type=tip} HAProxy SSL 支持 如果您不熟悉 HAProxy 并且在 bind 指令上使用 SSL 证书,则应将您的 SSL 证书、密钥和任何 CA 文件合并到单个 .pem 包中,并在 bind 指令中使用其路径。有关更多信息,请参阅 HAProxy 的 bind 文档。大多数 Let's Encrypt 客户端不会生成这样的文件,因此您可能需要自定义“发行后”脚本来为您执行此操作。 ::

Subdomain

此配置使用仅专用于 Strapi 的子域。它将正常的 HTTP 流量重定向到 SSL,并将所有请求(API 和管理员)代理到服务器上运行的 Strapi 服务器。

  • 示例域:api.example.com
  • 示例管理面板:api.example.com/admin
  • 示例 API:api.example.com/api
  • 示例上传文件(本地提供商):api.example.com/uploads
/etc/haproxy/haproxy.cfg
sh
global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA3$
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

# 此行以上的所有内容均为 HAProxy 默认值

frontend api.example.com
        bind *:80
        bind *:443 ssl crt /path/to/your/cert+key+ca.pem
        http-request redirect scheme https unless { ssl_fc }
        default_backend strapi-backend

backend strapi-backend
        server local 127.0.0.1:1337

子文件夹统一

此配置使用专用于 Strapi 的子文件夹。它将正常的 HTTP 流量重定向到 SSL,并将前端代理到 localhost:8080,但将 example.com/test 子路径上的所有 Strapi 请求代理到本地运行的 Strapi 应用程序。

HAProxy 无法提供静态内容,以下示例将前端流量代理到在本地主机端口 8080 上运行的其他 Web 服务器。

  • Example domain: example.com/test
  • Example admin panel: example.com/test/admin
  • Example API: example.com/test/api
  • Example uploaded Files (local provider): example.com/test/uploads
/etc/haproxy/haproxy.cfg
sh
global
  log /dev/log    local0
  log /dev/log    local1 notice
  chroot /var/lib/haproxy
  stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
  stats timeout 30s
  user haproxy
  group haproxy
  daemon

  # 默认 SSL 材料位置
  ca-base /etc/ssl/certs
  crt-base /etc/ssl/private

  # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
  ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA3$
  ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
  ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
  log     global
  mode    http
  option  httplog
  option  dontlognull
  timeout connect 5000
  timeout client  50000
  timeout server  50000
  errorfile 400 /etc/haproxy/errors/400.http
  errorfile 403 /etc/haproxy/errors/403.http
  errorfile 408 /etc/haproxy/errors/408.http
  errorfile 500 /etc/haproxy/errors/500.http
  errorfile 502 /etc/haproxy/errors/502.http
  errorfile 503 /etc/haproxy/errors/503.http
  errorfile 504 /etc/haproxy/errors/504.http

# 此行以上的所有内容均为 HAProxy 默认值

frontend example.com
  bind *:80
  bind *:443 ssl crt /path/to/your/cert+key+ca.pem
  http-request redirect scheme https unless { ssl_fc }
  acl test path_beg /test
  use_backend strapi-backend if test
  default_backend default-backend

backend default-backend
  # HAProxy 无法自行提供静态内容
  # 此示例正在将流量中继到其他后端 Web 服务器
  server somewebserver 127.0.0.1:8080

backend strapi-backend
  http-request set-path "%[path,regsub(^/test/?,/)]"
  server local 127.0.0.1:1337

将登录页面重定向到管理面板

如果您不希望将默认登录页面安装在 / 上,您可以使用下面的示例代码创建自定义中间件,以自动重定向到您的管理面板。

此示例配置要求管理面板可在 /admin 上访问。如果您使用上述配置之一将其更改为 /dashboard,您还需要调整此示例配置。

./config/middlewares.js
js
module.exports = ({ env }) => [
  // ...
  { resolve: '../src/middlewares/admin-redirect' },
]
./src/middlewares/admin-redirect.js
js
module.exports = (_config, { strapi }) => {
  const redirects = ['/', '/index.html'].map(path => ({
    method: 'GET',
    path,
    handler: ctx => ctx.redirect('/admin'),
    config: { auth: false },
  }))

  strapi.server.routes(redirects)
}