首页 > 解决方案 > 带有路由器的 ReactJS 应用程序可以托管在 S3 上并由 nginx 代理前端吗?

问题描述

我可能会扭曲一些可怕的事情,但是......我得到了一个必须提供给多个子域的 ReactJS 应用程序,所以

   a.foo.bar
   b.foo.bar
   c.foo.bar
   ...

这些中的每一个都应该指向应用程序的不同实例,但我不想npm start为每个实例运行 - 这将是一个疯狂的服务器资源量。

所以我去在 S3 上托管这些。我有一个存储桶foo.bar,然后在其下有目录a b c...并将该存储桶设置为服务静态网站。到目前为止一切顺利 - 如果我去https://s3.amazonaws.com/foo.bar/a/我会得到索引页面。然而,大多数事情往往会从那里中断,因为存在与/css/或之类的非相对链接/somepath- 这些中断是因为它们不够聪明,无法意识到它们是从 /foo.bar/a/ 提供服务的。另外,无论如何,我们都想要一个域。

所以现在我需要映射a.foo.bar-> https://s3.amazonaws.com/foo.bar/a/。我们没有使用 AWS 托管我们的域,所以我不确定是否可以使用 CloudFront 或类似的东西来处理它。按照这些思路打开解决方案,但我找不到它。

相反,我建立了一个简单的 nginx 代理。当我拥有代理时,我还添加了强制 https 和其他一些东西,形式如下:

server {
  listen       443;
  server_name  foo.bar;

  ssl                  on;
  ssl_certificate      /etc/pki/tls/certs/server.crt;
  ssl_certificate_key  /etc/pki/tls/certs/server.key;

  ssl_session_timeout  5m;

  ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers   on;

  # Redirect (*).foo.bar to (s3bucket)/(*)
  location / {
    index  index.html index.htm;

    set $legit "0";
    set $index "";

    # First off, we lose the index document functionality of S3 when we
    # proxy requests.  So we need to add that back on to our rewrites if
    # needed.  This is a little dangerous, probably should find a better
    # way if one exists.
    if ($uri ~* "\.foo\.bar$") {
      set $index "/index.html";
    }
    if ($uri ~* "\/$") {
      set $index "index.html";
    }

    # If we're making a request to foo.bar (not a sub-host),
    # make the request directly to "production"
    if ($host ~* "^foo\.bar") {
      set $legit "1";
      rewrite /(.*) /foo.bar/production/$1$index break;
    }

    # Otherwise, take the sub-host from the request and use that for the
    # redirect path
    if ($host ~* "^(.*?)\.foo\.bar") {
      set $legit "1";
      set $subhost $1;
      rewrite /(.*) /foo.bar/$subhost/$1$index break;
    }

    # Anything else, give them foo.bar
    if ($legit = "0") {
      return 302 https://foo.bar;
    }

    # Peform the actual proxy forward
    proxy_pass https://s3.amazonaws.com/;
    proxy_set_header Host s3.amazonaws.com;
    proxy_set_header Referer https://s3.amazonaws.com;

    proxy_set_header User-Agent $http_user_agent;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Accept-Encoding "";
    proxy_set_header Accept-Language $http_accept_language;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    sub_filter google.com example.com;
    sub_filter_once off;
  }
}

这行得通 - 我去 a.foo.bar,我得到了我期望的索引页面,然后点击工作。但是,应用程序的一部分也执行 OAuth 样式登录,并期望浏览器被重定向回页面 /reentry?token=foo... 问题是路径仅作为 React 应用程序中的路由存在,并且该应用程序不是由像 S3 这样的静态 Web 服务器加载的,因此您只会得到 404(或 403,因为我还没有定义或转发错误页面)。

所以....所有的问题...

我可以从像 S3 这样的哑/静态服务器为 ReactJS 应用程序提供服务,并让它理解对其路由的回调吗?请记住,S3 中的索引/错误指令似乎在以我上面的方式使用代理时被丢弃。

标签: reactjsnginxamazon-s3

解决方案


好的,我最初的问题有很多,但它的核心真的归结为:作为一个非 UI 人员,我如何使 OAuth 工作流与 React 应用程序一起工作?在这种情况下,回调 URL 是一个路由,如果您卸载 index.html 页面,则该路由不存在。如果您直接针对 S3,可以通过将所有错误定向到 index.html 来解决,这会重新加载路由并且回调起作用。

然而,当由 nginx 引导时,我们会丢失这个 error->index.html 路由。幸运的是,添加回来是一件非常简单的事情:

  location / {
    proxy_intercept_errors on;
    error_page 400 403 404 500 =200 /index.html;

可能不需要所有这些状态代码 - 对于 S3,最重要的是 403。当您请求一个不存在的页面时,它会将其视为您正在尝试浏览存储桶,并为您提供返回 403 禁止而不是 404 未找到或类似的东西。因此,在这种情况下,来自 S3 的导致 403 的响应将被重定向到 /index.html,这将调用在那里加载的路由,并且对 /callback?token=... 的回调将起作用。


推荐阅读