web-dev-qa-db-ja.com

S3静的WebサイトホスティングすべてのパスをIndex.htmlにルーティングする

S3を使用して、HTML5 pushStatesを使用するjavascriptアプリをホストしています。問題は、ユーザーがURLのいずれかをブックマークすると、何にも解決されないことです。必要なのは、完全なリダイレクトを行うだけでなく、すべてのurlリクエストを取得し、S3バケット内のルートindex.htmlを提供する機能です。その後、私のJavaScriptアプリケーションはURLを解析し、適切なページを提供できます。

リダイレクトする代わりに、すべてのURLリクエストに対してindex.htmlを提供するようにS3に指示する方法はありますか?これは、この例のように単一のindex.htmlを提供することにより、すべての着信要求を処理するようにApacheをセットアップするのと似ています。 https://stackoverflow.com/a/10647521/1762614 。これらのルートを処理するためだけにWebサーバーを実行することは避けたいと思います。 S3からすべてを行うことは非常に魅力的です。

208
Mark Nutter

CloudFrontのヘルプを使用すると、URLハッキングなしで簡単に解決できます。

  • S3バケットを作成します。たとえば、react
  • 次の設定でCloudFrontディストリビューションを作成します:
    • デフォルトのルートオブジェクト:index.html
    • Origin Domain Name:S3バケットドメイン、例:react.s3.amazonaws.com
  • エラーページタブに移動し、カスタムエラー応答の作成:をクリックします。
    • HTTPエラーコード:403:禁止(404:見つかりません、S3静的Webサイトの場合)
    • エラー応答のカスタマイズ:はい
    • 応答ページのパス:/index.html
    • HTTP応答コード:200:OK
    • 作成をクリックします
203
lenaten

これを機能させる方法は次のとおりです。

ドメインのS3コンソールのリダイレクトルールの編集セクションで、次のルールを追加します。

<RoutingRules>
  <RoutingRule>
    <Condition>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
    </Condition>
    <Redirect>
      <HostName>yourdomainname.com</HostName>
      <ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith>
    </Redirect>
  </RoutingRule>
</RoutingRules>

これにより、404が見つからないすべてのパスが、ハッシュドメインのパスでルートドメインにリダイレクトされます。つまり、 http://yourdomainname.com/posts は、 http://yourdomainname.com/#!/posts にリダイレクトされます。ただし、/ postsにファイルがない場合に限ります。

ただし、HTML5 pushStatesを使用するには、このリクエストを受け取り、ハッシュバンパスに基づいて適切なpushStateを手動で確立する必要があります。これをindex.htmlファイルの先頭に追加します。

<script>
  history.pushState({}, "entry page", location.hash.substring(1));
</script>

これによりハッシュが取得され、HTML5 pushStateに変換されます。この時点から、pushStatesを使用して、アプリに非ハッシュバンパスを含めることができます。

192
Mark Nutter

他の人が言及したS3 /リダイレクトベースのアプローチには、問題はほとんどありません。

  1. アプリのパスが解決されると、複数のリダイレクトが発生します。例:www.myapp.com/path/for/testはwww.myapp.com/#/path/for/testとしてリダイレクトされます
  2. SPAフレームワークのアクションによって「#」が出入りするため、URLバーにちらつきがあります。
  3. Seoは次の理由で影響を受けます。そのグーグルはリダイレクトに手を強いる
  4. アプリのSafariのサポートは手間がかかります。

解決策は次のとおりです。

  1. Webサイトにインデックスルートが設定されていることを確認してください。ほとんどはindex.htmlです
  2. S3構成からルーティングルールを削除する
  3. S3バケットの前にCloudfrontを配置します。
  4. Cloudfrontインスタンスのエラーページルールを構成します。エラールールで次を指定します。
    • HTTPエラーコード:404(および必要に応じて403またはその他のエラー)
    • エラーキャッシングの最小値TTL(秒):0
    • 応答のカスタマイズ:はい
    • 応答ページのパス:/index.html
    • HTTP応答コード:200

5. SEOのニーズ+ index.htmlがキャッシュされないことを確認するには、次の手順を実行します。

  • EC2インスタンスを構成し、nginxサーバーをセットアップします。
  • EC2インスタンスにパブリックIPを割り当てます。
  • インスタンスとして作成したEC2インスタンスを持つELBを作成します
  • ELBをDNSに割り当てることができるはずです。
  • 次に、nginxサーバーを次のことを行うように設定します:Proxy_passをCDN(index.htmlのみ、クラウドフロントから直接他のアセットに提供する)にすべてのリクエストを渡し、検索ボットの場合、Prerender.ioのようなサービスで規定されているようにトラフィックをリダイレクトします

Nginxのセットアップに関する詳細については、メモを残してください。それを苦労して学んだ。

クラウドフロント配布が更新されたら。クラウドフロントキャッシュを一度無効にして、初期モードにします。ブラウザでURLにアクセスすると、すべてが正常になります。

123
moha297

これは正接ですが、S3でホストしたい(HTML5)ブラウザー履歴で Rackt'sReact Router library を使用しているユーザー向けのヒントです。

ユーザーがS3がホストする静的Webサイトで/foo/bearにアクセスするとします。 David's以前の提案 の場合、リダイレクトルールはそれらを/#/foo/bearに送信します。ブラウザの履歴を使用してアプリケーションを構築する場合、これはあまり役に立ちません。ただし、この時点でアプリケーションがロードされ、履歴を操作できるようになりました。

Racktを含める history プロジェクトに(参照: Using Custom Histories from Reactルータープロジェクト)、次の例に示すように、ハッシュ履歴パスを認識するリスナーを追加し、必要に応じてパスを置き換えることができます。

import ReactDOM from 'react-dom';

/* Application-specific details. */
const route = {};

import { Router, useRouterHistory } from 'react-router';
import { createHistory } from 'history';

const history = useRouterHistory(createHistory)();

history.listen(function (location) {
  const path = (/#(\/.*)$/.exec(location.hash) || [])[1];
  if (path) history.replace(path);
});

ReactDOM.render(
  <Router history={history} routes={route}/>,
  document.body.appendChild(document.createElement('div'))
);

要点をまとめると:

  1. DavidのS3リダイレクトルールは、/foo/bear/#/foo/bearにリダイレクトします。
  2. アプリケーションがロードされます。
  3. 履歴リスナーは#/foo/bear履歴表記を検出します。
  4. そして、履歴を正しいパスに置き換えます。

Link tags は、他のすべてのブラウザー履歴機能と同様に、期待どおりに機能します。私が気づいた唯一の欠点は、最初のリクエストで発生するインタースティシャルリダイレクトです。

これは AngularJSのソリューション に触発されたもので、どのアプリケーションにも簡単に適応できると思います。

15
Michael Ahlers

この問題には4つの解決策があります。最初の3つはすでに回答でカバーされており、最後の1つは私の貢献です。

  1. エラードキュメントをindex.htmlに設定します
    問題:応答本文は正しいですが、ステータスコードは404になり、SEOを傷つけます。

  2. リダイレクトルールを設定します。
    問題:URLが#!で汚染され、ロード時にページがフラッシュします。

  3. CloudFrontの設定
    問題:すべてのページがOriginから404を返すため、何もキャッシュしない場合は選択する必要があります(推奨されるTTL 0 )またはサイトを更新するときにキャッシュして問題が発生する場合。

  4. すべてのページを事前レンダリングします。
    問題:特にページが頻繁に変更される場合に、ページを事前レンダリングするための追加作業。たとえば、ニュースWebサイト。

私の提案は、オプション4を使用することです。すべてのページを事前レンダリングすると、予想されるページで404エラーは発生しません。ページは正常にロードされ、フレームワークが制御を取り、SPAとして正常に機能します。エラードキュメントを設定して、一般的なerror.htmlページとリダイレクトルールを表示して、404エラーを404.htmlページに(ハッシュバングなしで)リダイレクトすることもできます。

403 Forbiddenエラーについては、まったく発生させません。私のアプリケーションでは、ホストバケット内のallファイルはパブリックであり、everyoneオプションでこれをreadパーミッション。サイトにプライベートのページがある場合、ユーザーにHTMLlayoutを表示させることは問題になりません。保護する必要があるのはdataで、これはバックエンドで行われます。

また、ユーザーの写真などのプライベートアセットがある場合は、anotherバケットに保存できます。プライベートアセットはdataと同じ注意が必要であり、アプリのホストに使用されるアセットファイルと比較できないためです。

11
Zanon

今日も同じ問題に遭遇しましたが、@ Mark-Nutterのソリューションは、私のanglejsアプリケーションからハッシュバンを削除するために不完全でした。

実際には、権限の編集に移動し、権限の追加をクリックする必要があります)そして、バケットに適切なListを全員に追加します。この設定により、AWS S3は404エラーを返すことができ、リダイレクトルールがケースを適切にキャッチします。

ちょうどこのような : - enter image description here

そして、リダイレクトルールの編集に行き、このルールを追加できます:

<RoutingRules>
    <RoutingRule>
        <Condition>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <HostName>subdomain.domain.fr</HostName>
            <ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
</RoutingRules>

ここで、HostNamesubdomain.domain.frをご使用のドメインに置き換え、KeyPrefix#!/ SEOの目的でhashbangメソッドを使用しない場合。

もちろん、angularアプリケーションで既にhtml5modeをセットアップしている場合にのみ、これらすべてが機能します。

$locationProvider.html5Mode(true).hashPrefix('!');
10
davidbonachera

Amazon S3から提供されるAngular 2+アプリケーションを作成し、直接URLを機能させる最も簡単なソリューションは、S3バケット設定でindex.htmlをIndexおよびErrorドキュメントとして指定することです。

enter image description here

8

問題はまだ残っているので、別の解決策を考えます。私の場合は、[mydomain]/pull-requests/[pr number] /でアクセス可能にするマージの前に、テストのためにすべてのプルリクエストをs3に自動デプロイしたかったということです。
(例:www.example.com/pull-requests/822/)

私の知る限り、s3ルールシナリオ以外では、html5ルーティングを使用して1つのバケットに複数のプロジェクトを含めることができるため、ルートフォルダー内のプロジェクトに対して最も多く投票された提案が機能しますが、独自のサブフォルダー内の複数のプロジェクトに対しては機能しません。

だから私は私のドメインをサーバーに向けて、そこでnginxの設定に従って仕事をした

location /pull-requests/ {
    try_files $uri @get_files;
}
location @get_files {
    rewrite ^\/pull-requests\/(.*) /$1 break;
    proxy_pass http://<your-Amazon-bucket-url>;
    proxy_intercept_errors on;
    recursive_error_pages on;
    error_page 404 = @get_routes;
}

location @get_routes {
    rewrite ^\/(\w+)\/(.+) /$1/ break;
    proxy_pass http://<your-Amazon-bucket-url>;
    proxy_intercept_errors on;
    recursive_error_pages on;
    error_page 404 = @not_found;
}

location @not_found {
    return 404;
}

ファイルを取得しようとしますが、見つからない場合はhtml5ルートであると想定して試行します。見つからないルートの404 angularページがある場合、@ not_foundに到達することはなく、見つからないファイルの代わりにangular 404ページが返されます。 @get_routesまたは何かのルール。

私はnginxの設定の領域であまり快適に感じていないと言わなければなりません、そのためにいくつかの試行錯誤でこれを動作させたので、これが機能する間、改善の余地があると確信し、あなたの考えを共有してください。

:S3構成に含まれていた場合、s3リダイレクトルールを削除します。

btwはSafariで動作します

4
Andrew Arnautov

同じ種類の問題を探していました。最終的に、上記の推奨ソリューションを組み合わせて使用​​しました。

まず、複数のフォルダーを持つs3バケットがあります。各フォルダーは、react/redux Webサイトを表します。キャッシュの無効化にもcloudfrontを使用しています。

したがって、404をサポートするためにルーティングルールを使用し、それらをハッシュ設定にリダイレクトする必要がありました。

<RoutingRules>
    <RoutingRule>
        <Condition>
            <KeyPrefixEquals>website1/</KeyPrefixEquals>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <Protocol>https</Protocol>
            <HostName>my.Host.com</HostName>
            <ReplaceKeyPrefixWith>website1#</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
    <RoutingRule>
        <Condition>
            <KeyPrefixEquals>website2/</KeyPrefixEquals>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <Protocol>https</Protocol>
            <HostName>my.Host.com</HostName>
            <ReplaceKeyPrefixWith>website2#</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
    <RoutingRule>
        <Condition>
            <KeyPrefixEquals>website3/</KeyPrefixEquals>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <Protocol>https</Protocol>
            <HostName>my.Host.com</HostName>
            <ReplaceKeyPrefixWith>website3#</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
</RoutingRules>

私のjsコードでは、react-routerのbaseName configでそれを処理する必要がありました。まず、依存関係が相互運用可能であることを確認します。history==4.0.0と互換性のないreact-router==3.0.1をインストールしました。

私の依存関係は次のとおりです。

  • 「history」:「3.2.0」、
  • 「反応」:「15.4.1」、
  • 「react-redux」:「4.4.6」、
  • 「react-router」:「3.0.1」、
  • 「react-router-redux」:「4.0.7」、

履歴を読み込むためのhistory.jsファイルを作成しました:

import {useRouterHistory} from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';

export const browserHistory = useRouterHistory(createBrowserHistory)({
    basename: '/website1/',
});

browserHistory.listen((location) => {
    const path = (/#(.*)$/.exec(location.hash) || [])[1];
    if (path) {
        browserHistory.replace(path);
    }
});

export default browserHistory;

このコードにより、サーバーによって送信された404をハッシュで処理し、ルートをロードするために履歴内でそれらを置き換えることができます。

これで、このファイルを使用して、ストアとルートファイルを構成できます。

import {routerMiddleware} from 'react-router-redux';
import {applyMiddleware, compose} from 'redux';

import rootSaga from '../sagas';
import rootReducer from '../reducers';

import {createInjectSagasStore, sagaMiddleware} from './redux-sagas-injector';

import {browserHistory} from '../history';

export default function configureStore(initialState) {
    const enhancers = [
        applyMiddleware(
            sagaMiddleware,
            routerMiddleware(browserHistory),
        )];

    return createInjectSagasStore(rootReducer, rootSaga, initialState, compose(...enhancers));
}
import React, {PropTypes} from 'react';
import {Provider} from 'react-redux';
import {Router} from 'react-router';
import {syncHistoryWithStore} from 'react-router-redux';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import variables from '!!sass-variable-loader!../../../css/variables/variables.prod.scss';
import routesFactory from '../routes';
import {browserHistory} from '../history';

const muiTheme = getMuiTheme({
    palette: {
        primary1Color: variables.baseColor,
    },
});

const Root = ({store}) => {
    const history = syncHistoryWithStore(browserHistory, store);
    const routes = routesFactory(store);

    return (
        <Provider {...{store}}>
            <MuiThemeProvider muiTheme={muiTheme}>
                <Router {...{history, routes}} />
            </MuiThemeProvider>
        </Provider>
    );
};

Root.propTypes = {
    store: PropTypes.shape({}).isRequired,
};

export default Root;

それが役に立てば幸い。この構成では、reduxインジェクターと自家製のsagasインジェクターを使用して、ルーティングを介してJavaScriptを非同期に読み込みます。これらの行を気にしないでください。

3
Guillaume Cisco

ReactルーターとAWS Amplify Consoleで動作するソリューションを探してここに着いた場合、Amplify ConsoleはアプリのCloudFront Distributionを公開しないため、CloudFrontリダイレクトルールを直接使用できないことを既に知っています。

ただし、解決策は非常に簡単です。AmplifyConsoleで次のようにリダイレクト/書き換えルールを追加するだけです。

Amplify Console Rewrite rule for React Router

詳細については、次のリンクを参照してください(およびスクリーンショットのコピーに適したルール):

1
Yaro

私はこれに対する答えを自分で探していました。 S3はリダイレクトのみをサポートしているように見えます。URLを書き換えて、静かに別のリソースを返すことはできません。ビルドスクリプトを使用して、必要なすべてのパスの場所にindex.htmlのコピーを作成することを検討しています。たぶんそれもあなたのために働くでしょう。

0
Ed Thomas

これは 最もエレガントなソリューション 私が見つけたものです-ワイルドカードリダイレクトでアプリルーターモジュールを使用します。

{ path: '**', redirectTo: '' }

次に、上記の無数の投稿で述べたように、404/403エラーを200ステータスのindex.htmlにリダイレクトしていることを確認してください。問題は、これによりブラウザが更新され、ベースhrefが(href +前のルート)として読み込まれることです。ルータービューを表示している場合

www.my-app.com/homeその後、更新が表示されます

www.my-app.com/home/home

重複したルートパスを削除するには、APP_BASE_HREFモジュールを使用してブラウザのベースhrefを再割り当てします このように

最初のurlパラメーターを保持する必要がある場合は、'/'分割から複数の結果を追加します。

ブラウザがwww.your-app.com/home/homeのSPAリダイレクトにヒットすると、URLがwww.your-app.com/homeに置き換えられ、アプリはアプリ内ルーティング設定から期待どおりに動作します

0
Chris Concannon

非常に簡単な答えを入れてください。 S3でホストしている場合は、ルーターのハッシュロケーション戦略を使用します。

export const AppRoutingModule:ModuleWithProviders = RouterModule.forRoot(routes、{useHash:true、scrollPositionRestoration: 'enabled'});

0
Meester Over