S3静态网站托pipepath到Index.html的所有path
我正在使用S3来托pipe一个JavaScript应用程序,将使用HTML5 pushStates。 问题是,如果用户书签的任何url,它不会解决任何事情。 我需要的是能够接受所有的url请求,并在我的S3存储桶中提供根index.html,而不仅仅是做一个完整的redirect。 然后我的JavaScript应用程序可以parsingURL并提供正确的页面。
有什么办法可以告诉S3为所有URL请求提供index.html而不是redirect? 这与设置apache来处理所有传入的请求类似,如下例所示: https : //stackoverflow.com/a/10647521/1762614 。 我真的想避免运行一个Web服务器来处理这些路线。 做S3的一切都非常有吸引力。
马克解决scheme并不坏,但有一个更简单的解决scheme。 错误文档中的存储桶属性只需使用与索引文档相同的文件。 像Backbone,AngularJS等JavaScript框架将以这种方式开箱即用,页面刷新将得到完全支持。
我能够得到这个工作的方式如下:
在您的域的S3控制台的“ 编辑redirect规则”部分中,添加以下规则:
<RoutingRules> <RoutingRule> <Condition> <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals> </Condition> <Redirect> <HostName>yourdomainname.com</HostName> <ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith> </Redirect> </RoutingRule> </RoutingRules>
这将redirect所有path导致一个404找不到你的根域与path的散列爆炸版本。 所以http://yourdomainname.com/posts会redirect到http://yourdomainname.com/#!/posts,前提是在/ posts没有文件。
然而,要使用HTML5 pushState,我们需要接受这个请求,并根据hash-bangpath手动build立合适的pushState。 因此,将其添加到index.html文件的顶部:
<script> history.pushState({}, "entry page", location.hash.substring(1)); </script>
这抓住散列并将其转换为HTML5 pushState。 从这一点上,你可以使用pushState在你的应用程序中使用非hash-bangpath。
其他人提到的基于S3 / Redirect的方法几乎没有问题。
- 多重redirect发生在您的应用程序的path已解决。 例如:www.myapp.com/path/for/test被redirect为www.myapp.com/#/path/for/test
- url栏中有一个闪烁的字符,因为你的SPA框架的作用是“#”来的。
- seo受到影响,因为 – “嘿! 它的谷歌强迫他的手redirect“
- Safari支持您的应用程序的折腾。
解决scheme是:
- 确保你有为你的网站configuration的索引路线。 大部分是index.html
- 从S3configuration中删除路由规则
- 将Cloudfront放在您的S3存储桶前。
- 为您的Cloudfront实例configuration错误页面规则。 在错误规则中指定:
- HTTP错误代码:404(和403或其他错误根据需要)
- 错误caching最小TTL(秒):0
- 自定义响应:是的
- 响应页面path:/index.html
- HTTP响应码:200
5.对于SEO需求+确保您的index.html不caching,请执行以下操作:
- configuration一个EC2实例并设置一个nginx服务器。
- 将一个公共IP分配给您的EC2实例。
- 创build一个具有您创build的EC2实例作为实例的ELB
- 您应该能够将ELB分配给您的DNS。
- 现在,configuration您的nginx服务器执行以下操作:Proxy_pass对CDN的所有请求(仅适用于index.html,直接从您的云端服务器提供其他资源)以及search漫游器,根据服务规定redirectstream量,如Prerender.io
关于nginx的设置,我可以帮助更多的细节,只是留下一个笔记。 已经学会了这个难题。
一旦云端发布更新。 使您的云端caching无效一次处于原始模式。 打在浏览器的url,一切都应该是好的。
在CloudFront的帮助下,无需url黑客就可以轻松解决这个问题。
- 创buildS3存储桶,例如:反应
- 使用以下设置创buildCloudFront分配:
- 默认的根对象 :index.html
- 起源域名 :S3存储域,例如:react.s3.amazonaws.com
- 转到错误页面选项卡,单击创build自定义错误响应 :
- HTTP错误代码 :403:禁止(404:未find,在S3静态网站的情况下)
- 自定义错误响应 :是
- 响应页面path :/index.html
- HTTP响应代码 :200:确定
- 点击创build
这是切线,但这里给那些使用Rackt的 React路由器库 (HTML5)浏览器历史logging谁想要在S3上承载的小费。
假设用户访问S3托pipe的静态网站上的/foo/bear
。 鉴于David 之前的build议 ,redirect规则将把它们发送到/#/foo/bear
。 如果您的应用程序是使用浏览器历史logging构build的,则不会有太大好处。 然而,你的应用程序在这一点上加载,它现在可以操纵历史。
在我们的项目中包括Rackt 历史logging (另请参阅使用 React Router项目中的自定义历史logging ),可以添加一个了解哈希历史path的侦听器,并根据需要replacepath,如下例所示:
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')) );
回顾一下:
- 大卫的S3redirect规则将直接
/foo/bear
/#/foo/bear
。 - 您的应用程序将加载。
- 历史监听器将检测
#/foo/bear
历史符号。 - 并用正确的pathreplace历史。
Link
标签将按预期工作,所有其他浏览器历史function也一样。 我注意到唯一的缺点是在初始请求时发生插页式redirect。
这是AngularJS的解决scheme的启发,我怀疑可以很容易地适应任何应用程序。
我今天遇到了同样的问题,但@ Mark-Nutter的解决scheme是不完整的从我的angularjs应用程序中删除hashbang。
事实上,你必须去编辑权限 ,点击添加更多的权限 ,然后将你的桶上的正确列表添加到每个人。 使用此configuration,AWS S3现在将能够返回404错误,然后redirect规则将正确地处理这种情况。
像这样 :
然后你可以去编辑redirect规则并添加这个规则:
<RoutingRules> <RoutingRule> <Condition> <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals> </Condition> <Redirect> <HostName>subdomain.domain.fr</HostName> <ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith> </Redirect> </RoutingRule> </RoutingRules>
在这里,您可以将您的域名和KeyPrefix #!/replace为HostName subdomain.domain.fr,如果您不使用hashbang方法进行SEO目的。
当然,如果您已经在angular度应用程序中设置了html5mode,所有这些都将会起作用。
$locationProvider.html5Mode(true).hashPrefix('!');
由于问题仍然存在,我虽然我扔了另一种解决scheme。 我的情况是,我想要自动部署所有拉请求s3进行testing之前合并使他们可以在[mydomain] / pull-requests / [pr number] /
(例如www.example.com/pull-requests/822/)
据我所知,非s3规则场景将允许使用html5路由将多个项目放在一个存储桶中,因此,当大多数投票build议对根文件夹中的项目起作用时,它不适用于自己的子文件夹中的多个项目。
所以我把我的域名指向了我的服务器,在那里,nginxconfiguration完成了这个工作
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路线,并试图。 如果你有一个没有find路由的404angular度页面,你将永远不会到达@not_found,并让你返回angular度404页面,而不是找不到文件,这可以通过@get_routes或某些东西来解决。
我不得不说在nginxconfiguration区域感觉不太舒服,并且使用正则expression式来处理这个问题,所以我在一些试验和错误的工作中做了这个工作,所以在这个工作的时候我相信还有改进的空间,请分享你的想法。
注意 :如果您在S3configuration中使用s3redirect规则, 请删除它们。
并btw在Safari浏览器工作
使用Amazon S3提供的Angular 2+应用程序最简单的解决scheme是直接使用URL作为索引和错误文档。
我看到了4个解决这个问题的方法。 前三个答案已经覆盖,最后一个是我的贡献。
-
将错误文档设置为index.html。
问题 :响应主体将是正确的,但状态代码将是404,这会伤害search引擎优化。 -
设置redirect规则。
问题 :使用#!
污染的url 加载时页面闪烁。 -
configurationCloudFront。
问题 :所有页面将从原始位置返回404,因此您需要select是否不caching任何内容(build议使用TTL 0),或者在更新站点时caching并出现问题。 -
预渲染所有页面。
问题 :额外的预渲染页面的工作,特别是当页面频繁更改时。 例如,一个新闻网站。
我的build议是使用选项4.如果您prerender所有页面,将不会有404期望的页面错误。 页面将加载正常,框架将控制并正常作为一个SPA。 您还可以设置错误文档以显示通用的error.html页面和redirect规则,以将404错误redirect到404.html页面(不带hashbang)。
关于403禁止的错误,我不会让他们发生。 在我的应用程序中,我认为主机存储桶中的所有文件都是公共的,我使用读取权限设置了每个人的选项。 如果您的网站的页面是私人的,让用户看到HTML 布局不应该是一个问题。 你需要保护的是数据 ,这是在后端完成的。
另外,如果您拥有私人资产(如用户照片),则可以将其保存在另一个存储分区中。 由于私有资产需要与数据一样的谨慎,并且无法与用于托pipe应用的资产文件进行比较。
正在寻找同样的问题。 我结束了使用上述build议的解决scheme的混合。
首先,我有一个s3桶与多个文件夹,每个文件夹代表一个react / redux网站。 我也使用云端caching失效。
所以我不得不使用路由规则来支持404并将它们redirect到哈希configuration:
<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代码中,我需要用一个baseName
configuration来处理react-router。 首先,确保你的依赖是可互操作的,我已经安装了history==4.0.0
这与react-router==3.0.1
不兼容。
我的依赖是:
- “历史”:“3.2.0”,
- “反应”:“15.4.1”,
- “react-redux”:“4.4.6”,
- “react-router”:“3.0.1”,
- “react-router-redux”:“4.0.7”,
我创build了一个history.js
文件来加载历史logging:
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哈希,并replace它们在历史上加载我们的路线。
你现在可以使用这个文件来configuration你的商店和你的根文件。
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;
希望它有帮助。 你会注意到这个configuration我使用了redux注入器和一个自制sagas注入器,通过路由asynchronous加载javascript。 不要介意这些线。
我正在寻找自己的答案。 S3似乎只支持redirect,你不能只是重写URL并默默地返回一个不同的资源。 我正在考虑使用我的构build脚本来简单地在所有必需的path位置复制我的index.html。 也许这也适用于你。