El alojamiento de sitios web estáticos S3 dirige todas las rutas a Index.html
Estoy usando S3 para alojar una aplicación javascript que usará HTML5 pushStates. El problema es que si el usuario marca alguna de las URL como favorita, no se resolverá nada. Lo que necesito es la capacidad de aceptar todas las solicitudes de URL y publicar el index.html raíz en mi depósito S3, en lugar de simplemente realizar una redirección completa. Entonces mi aplicación javascript podría analizar la URL y mostrar la página adecuada.
¿Hay alguna forma de indicarle a S3 que proporcione index.html para todas las solicitudes de URL en lugar de realizar redirecciones? Esto sería similar a configurar Apache para manejar todas las solicitudes entrantes entregando un único index.html como en este ejemplo: https://stackoverflow.com/a/10647521/1762614 . Realmente me gustaría evitar ejecutar un servidor web sólo para manejar estas rutas. Hacer todo desde S3 es muy atractivo.
Es muy fácil solucionarlo sin hacks de URL, con la ayuda de CloudFront.
- Cree un depósito S3, por ejemplo: reaccionar
- Cree distribuciones de CloudFront con esta configuración:
- Objeto raíz predeterminado : index.html
- Nombre de dominio de origen : dominio del depósito S3, por ejemplo: reaccionar.s3.amazonaws.com
- Vaya a la pestaña Páginas de error , haga clic en Crear respuesta de error personalizada :
- Código de error HTTP : 403: Prohibido (404: No encontrado, en el caso de un sitio web estático S3)
- Personalizar respuesta de error : Sí
- Ruta de la página de respuesta : /index.html
- Código de respuesta HTTP : 200: Aceptar
- Haga clic en crear
La forma en que pude hacer que esto funcionara es la siguiente:
En la sección Editar reglas de redireccionamiento de la Consola S3 para su dominio, agregue las siguientes reglas:
<RoutingRules>
<RoutingRule>
<Condition>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<HostName>example.com</HostName>
<ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
</RoutingRules>
Esto redirigirá todas las rutas que resulten en un 404 no encontrado a su dominio raíz con una versión hash-bang de la ruta. Por lo tanto http://example.com/posts
, se redirigirá a http://example.com/#!/posts
siempre que no haya ningún archivo en /posts.
Sin embargo, para usar HTML5 pushStates, debemos tomar esta solicitud y establecer manualmente el pushState adecuado según la ruta hash-bang. Así que agrega esto al principio de tu index.html
archivo:
<script>
history.pushState({}, "entry page", location.hash.substring(1));
</script>
Esto toma el hash y lo convierte en un pushState HTML5. A partir de este momento, puede usar pushStates para tener rutas que no sean hash-bang en su aplicación.
Hay algunos problemas con el enfoque basado en S3/Redirect mencionado por otros.
- Se producen múltiples redirecciones a medida que se resuelven las rutas de su aplicación. Por ejemplo:
www.example.com/path/for/test
es redirigido comowww.example.com/#/path/for/test
- Hay un parpadeo en la barra de URL a medida que
#
va y viene debido a la acción de su marco SPA. - El SEO se ve afectado porque: '¡Oye! Es Google forzando su mano en las redirecciones.
- El soporte de Safari para tu aplicación se va a pique.
La solucion es:
- Asegúrese de tener configurada la ruta de índice para su sitio web. Mayormente es
index.html
- Eliminar reglas de enrutamiento de las configuraciones de S3
- Coloque un CloudFront frente a su depósito S3.
- Configure reglas de página de error para su instancia de CloudFront. En las reglas de error especifique:
- Código de error HTTP: 404 (y 403 u otros errores según sea necesario)
- Error de almacenamiento en caché TTL mínimo (segundos): 0
- Personalizar respuesta: Sí
- Ruta de la página de respuesta:
/index.html
- Código de respuesta HTTP: 200
- Para las necesidades de SEO + asegurarse de que
index.html
no se almacene en caché, haga lo siguiente:
- Configure una instancia EC2 y configure un servidor Nginx.
- Asigne una IP pública a su instancia EC2.
- Cree un ELB que tenga la instancia EC2 que creó como instancia
- Debería poder asignar el ELB a su DNS.
- Ahora, configure su servidor Nginx para hacer lo siguiente: Proxy_pass todas las solicitudes a su CDN (
index.html
solo para servir otros activos directamente desde su CloudFront) y para los robots de búsqueda, redirigir el tráfico según lo estipulado por servicios como Prerender.io.
Puedo ayudar con más detalles con respecto a la configuración de Nginx, solo deje una nota. Lo he aprendido de la manera más difícil.
Una vez que se actualice la distribución del frente de la nube. Invalide su caché de CloudFront una vez para estar en el modo original. Presione la URL en el navegador y todo debería estar bien.
Es tangencial, pero aquí hay un consejo para aquellos que usan la biblioteca React Router de Rackt con historial de navegador (HTML5) y desean alojar en S3.
Supongamos que un usuario visita /foo/bear
su sitio web estático alojado en S3. Dada la sugerencia anterior de David , las reglas de redireccionamiento los enviarán a . Si su aplicación se creó utilizando el historial del navegador, esto no servirá de mucho. Sin embargo, su aplicación está cargada en este punto y ahora puede manipular el historial./#/foo/bear
Al incluir el historial de Rackt en nuestro proyecto (consulte también Uso de historiales personalizados del proyecto React Router), puede agregar un oyente que conozca las rutas del historial hash y reemplazar la ruta según corresponda, como se ilustra en este ejemplo:
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'))
);
Recordar:
- La regla de redireccionamiento S3 de David dirigirá
/foo/bear
a/#/foo/bear
. - Su aplicación se cargará.
- El oyente histórico detectará la
#/foo/bear
notación histórica. - Y reemplace la historia con el camino correcto.
Link
Las etiquetas funcionarán como se esperaba, al igual que todas las demás funciones del historial del navegador. El único inconveniente que he notado es la redirección intersticial que se produce en la solicitud inicial.
Esto se inspiró en una solución para AngularJS y sospecho que podría adaptarse fácilmente a cualquier aplicación.
Ahora puedes hacer esto con Lambda@Edge para reescribir las rutas.
Aquí hay una función lambda@Edge que funciona:
- Cree una nueva función Lambda, pero utilice uno de los Blueprints preexistentes en lugar de una función en blanco.
- Busque "cloudfront" y seleccione generación de respuesta de cloudfront en los resultados de la búsqueda.
- Después de crear la función, reemplace el contenido con lo siguiente. También tuve que cambiar el tiempo de ejecución del nodo a 10.x porque cloudfront no admitía el nodo 12 en el momento de escribir este artículo.
'use strict';
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda@Edge
var request = event.Records[0].cf.request;
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/\/$/, '\/index.html');
// Log the URI as received by CloudFront and the new URI to be used to fetch from origin
console.log("Old URI: " + olduri);
console.log("New URI: " + newuri);
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
return callback(null, request);
};
En sus comportamientos frente a la nube, los editará para agregar una llamada a esa función lambda en "Solicitud de espectador".
Tutorial completo: https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/