Comment définir un objet racine par défaut pour les sous-répertoires d'un site Web hébergé statiquement sur Cloudfront?

99

Comment définir un objet racine par défaut pour les sous-répertoires sur un site Web hébergé statiquement sur Cloudfront? Plus précisément, j'aimerais www.example.com/subdir/index.htmlêtre servi chaque fois que l'utilisateur le demande www.example.com/subdir. Notez qu'il s'agit de fournir un site Web statique conservé dans un compartiment S3. De plus, je souhaite utiliser une identité d'accès d'origine pour limiter l'accès au compartiment S3 à Cloudfront uniquement.

Maintenant, je suis conscient que Cloudfront fonctionne différemment des États S3 et Amazon spécifiquement :

Le comportement des objets racine par défaut de CloudFront est différent du comportement des documents d'index Amazon S3. Lorsque vous configurez un compartiment Amazon S3 en tant que site Web et spécifiez le document d'index, Amazon S3 renvoie le document d'index même si un utilisateur demande un sous-répertoire dans le compartiment. (Une copie du document d'index doit apparaître dans chaque sous-répertoire.) Pour plus d'informations sur la configuration des compartiments Amazon S3 en tant que sites Web et sur les documents d'index, consultez le chapitre Hébergement de sites Web sur Amazon S3 dans le Amazon Simple Storage Service Developer Guide.

En tant que tel, même si Cloudfront nous permet de spécifier un objet racine par défaut, cela ne fonctionne que pour www.example.comet pas pour www.example.com/subdir. Afin de contourner cette difficulté, nous pouvons changer le nom de domaine d'origine pour qu'il pointe vers le point de terminaison du site Web donné par S3. Cela fonctionne très bien et permet aux objets racine d'être spécifiés de manière uniforme. Malheureusement, cela ne semble pas compatible avec les identités d'accès à l'origine . Plus précisément, les liens ci-dessus indiquent:

Passer en mode édition:

Distributions Web - Cliquez sur l'onglet Origines, cliquez sur l'origine que vous souhaitez modifier, puis sur Modifier. Vous ne pouvez créer une identité d'accès à l'origine que pour les origines dont le type d'origine est Origine S3.

Fondamentalement, pour définir le bon objet racine par défaut, nous utilisons le point de terminaison du site Web S3 et non le compartiment du site Web lui-même. Cela n'est pas compatible avec l'utilisation de l'identité d'accès à l'origine. En tant que tel, mes questions se résument soit à

  1. Est-il possible de spécifier un objet racine par défaut pour tous les sous-répertoires d'un site Web hébergé statiquement sur Cloudfront?

  2. Est-il possible de configurer une identité d'accès à l'origine pour le contenu servi à partir de Cloudfront où l'origine est un point de terminaison de site Web S3 et non un compartiment S3?

wyer33
la source
1
Je pense que c'est maintenant faisable avec Lambda @ edge, en utilisant une fonction qui redirige toutes les URL se terminant par / vers /index.html Je vais l'essayer sur mon site Web et rendre compte des résultats et publier la configuration détaillée en tant que réponse.
Cristian Măgherușan-Stanciu

Réponses:

2

MISE À JOUR: Il semble que j'étais incorrect! Voir la réponse de JBaczuk, qui devrait être la réponse acceptée sur ce fil.

Malheureusement, la réponse à vos deux questions est non.

1. Est-il possible de spécifier un objet racine par défaut pour tous les sous-répertoires d'un site Web hébergé statiquement sur Cloudfront?

Non. Comme indiqué dans la documentation AWS CloudFront ...

... Si vous définissez un objet racine par défaut, une demande d'utilisateur final pour un sous-répertoire de votre distribution ne renvoie pas l'objet racine par défaut. Par exemple, supposons index.htmlqu'il s'agisse de votre objet racine par défaut et que CloudFront reçoive une demande de l'utilisateur final pour le répertoire d'installation sous votre distribution CloudFront:

http://d111111abcdef8.cloudfront.net/install/

CloudFront ne retournera pas l'objet racine par défaut, même si une copie de index.htmlapparaît dans le répertoire d'installation.

...

Le comportement des objets racine par défaut de CloudFront est différent du comportement des documents d'index Amazon S3. Lorsque vous configurez un compartiment Amazon S3 en tant que site Web et spécifiez le document d'index, Amazon S3 renvoie le document d'index même si un utilisateur demande un sous-répertoire dans le compartiment. (Une copie du document d'index doit apparaître dans chaque sous-répertoire.)

2. Est-il possible de configurer une identité d'accès à l'origine pour le contenu servi à partir de Cloudfront où l'origine est un point de terminaison de site Web S3 et non un compartiment S3?

Pas directement. Vos options pour les origines avec CloudFront sont les compartiments S3 ou votre propre serveur.

C'est cette deuxième option qui ouvre cependant des possibilités intéressantes. Cela va probablement à l'encontre de l'objectif de ce que vous essayez de faire, mais vous pouvez configurer votre propre serveur dont le seul travail est d'être un serveur d'origine CloudFront.

Lorsqu'une demande arrive pour http://d111111abcdef8.cloudfront.net/install/ , CloudFront transmettra cette demande à votre serveur d'origine, en vous demandant /install. Vous pouvez configurer votre serveur d'origine comme vous le souhaitez, y compris pour servir index.htmldans ce cas.

Ou vous pouvez écrire une petite application Web qui prend juste cet appel et l'obtient de toute façon directement à partir de S3.

Mais je me rends compte que la configuration de votre propre serveur et le souci de le mettre à l'échelle peuvent aller à l'encontre de l'objectif de ce que vous essayez de faire en premier lieu.

Josh Padnick
la source
Le seul problème que j'ai avec cela est que cela signifie que vous auriez deux (2) URL capables d'accéder à votre site Web sur s3. Votre URL frontale cloud et votre URL s3 (bucket_name.s3-website-us-east-1.amazonaws.com)
Hayden
222

Il EST une façon de le faire. Au lieu de le pointer vers votre bucket en le sélectionnant dans la liste déroulante (www.example.com.s3.amazonaws.com), pointez-le vers le domaine statique de votre bucket (par exemple www.example.com.s3-website-us -west-2.amazonaws.com):

entrez la description de l'image ici

Merci à ce fil de discussion du forum AWS

JBaczuk
la source
6
Quelqu'un sait-il si cela se facture différemment avec une origine s3 et une origine Web?
fideloper
3
Cela fonctionne-t-il bien si je souhaite diffuser tout mon site Web et mes fichiers HTTPSuniquement?
Manjit Kumar le
3
Cela signifie-t-il que le S3 doit être activé en tant que serveur Web?
Anthony Kong
6
OP a explicitement déclaré que cette approche ne fonctionnera pas pour lui: "Afin de contourner cette difficulté, nous pouvons changer le nom de domaine d'origine pour qu'il pointe vers le point de terminaison du site Web donné par S3. Cela fonctionne très bien et permet aux objets racine d'être spécifiés de manière uniforme. Malheureusement , cela ne semble pas compatible avec les identités d'accès à l'origine ". AWS eux-mêmes semblent recommander lamda @ edge pour cela - aws.amazon.com/blogs/compute/…
icyitscold
3
Ce n'est pas compatible Cloud Front - Origin Access Identity. Vous ne pourrez pas restreindre l'accès à votre compartiment S3 de cette façon.
rocketspacer
15

L'activation de l'hébergement S3 signifie que vous devez ouvrir le seau au monde. Dans mon cas, je devais garder le bucket privé et utiliser la fonctionnalité d'identité d'accès à l'origine pour restreindre l'accès à Cloudfront uniquement. Comme @Juissi l'a suggéré, une fonction Lambda peut corriger les redirections:

'use strict';

/**
 * Redirects URLs to default document. Examples:
 *
 * /blog            -> /blog/index.html
 * /blog/july/      -> /blog/july/index.html
 * /blog/header.png -> /blog/header.png
 *
 */

let defaultDocument = 'index.html';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if(request.uri != "/") {
        let paths = request.uri.split('/');
        let lastPath = paths[paths.length - 1];
        let isFile = lastPath.split('.').length > 1;

        if(!isFile) {
            if(lastPath != "") {
                request.uri += "/";
            }

            request.uri += defaultDocument;
        }

        console.log(request.uri);
    }

    callback(null, request);
};

Après avoir publié votre fonction, accédez à votre distribution cloudfront dans la console AWS. Allez dans Behaviors, puis choisissez Origin Requestsous Lambda Function Associationset collez enfin l'ARN dans votre nouvelle fonction.

Kenske
la source
5
Il est prêt à déployer lambda fonction similaire à celle - ci: serverlessrepo.aws.amazon.com/applications/...
marcanuy
Le problème ici est que cette fonction doit être déployée sur us-east-1, donc si vous avez une entreprise soumise à une réglementation stricte du RGPD qui ne permet pas un seul morceau en dehors de l'Allemagne, ce n'est pas pour vous.
Renato Gama
5

Il existe une autre façon d'obtenir un fichier par défaut servi dans un sous-répertoire, comme example.com/subdir/. Vous pouvez en fait (par programme) stocker un fichier avec la clé subdir/dans le compartiment. Ce fichier n'apparaîtra pas dans la console de gestion S3, mais il existe réellement et CloudFront le servira.

Johan Gorter
la source
S3 converst subdir / to subdir; lorsque vous essayez de télécharger le HTML. De plus, lorsque vous essayez d'accéder à example.com/subdir/, cela échoue et si vous essayez d'accéder à example.com/subdir; il télécharge le fichier HTML au lieu de le rendre.
jacobfogg
4

La solution de contournement du problème consiste à utiliser lambda @ edge pour réécrire les demandes. Il suffit de configurer le lambda pour l'événement de requête de la visionneuse de la distribution CloudFront et de réécrire tout ce qui se termine par «/» ET n'est pas égal à «/» avec le document racine par défaut, par exemple index.html.

Juissi
la source
Plus de détails concernant cette approche ici: aws.amazon.com/blogs/compute/…
Henrik Aasted Sørensen
malheureusement Lambda @ Edge ne fonctionne que sur la région us-east-1, source: github.com/awslabs/serverless-application-model/issues/635
mruanova le
4

Il existe un guide «officiel» publié sur le blog AWS qui recommande de configurer une fonction Lambda @ Edge déclenchée par votre distribution CloudFront:

Bien sûr, c'est une mauvaise expérience utilisateur de s'attendre à ce que les utilisateurs tapent toujours index.html à la fin de chaque URL (ou même sachent qu'il devrait être là). Jusqu'à présent, il n'existait pas de moyen simple de fournir ces URL plus simples (équivalentes à la directive DirectoryIndex dans une configuration de serveur Web Apache) aux utilisateurs via CloudFront. Pas si vous souhaitez toujours pouvoir restreindre l'accès à l'origine S3 à l'aide d'un OAI. Cependant, avec la sortie de Lambda @ Edge, vous pouvez utiliser une fonction JavaScript exécutée sur les nœuds de périphérie CloudFront pour rechercher ces modèles et demander la clé d'objet appropriée à l'origine S3.

Solution

Dans cet exemple, vous utilisez la puissance de calcul au niveau de la périphérie CloudFront pour inspecter la demande lorsqu'elle provient du client. Réécrivez ensuite la requête afin que CloudFront demande un objet d'index par défaut (index.html dans ce cas) pour tout URI de requête qui se termine par «/».

Lorsqu'une requête est effectuée sur un serveur Web, le client spécifie l'objet à obtenir dans la requête. Vous pouvez utiliser cet URI et lui appliquer une expression régulière afin que ces URI soient résolus en un objet d'index par défaut avant que CloudFront ne demande l'objet à l'origine. Utilisez le code suivant:

'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 to CloudFront
    return callback(null, request);

};

Suivez le guide lié ci-dessus pour voir toutes les étapes requises pour configurer cela, y compris le compartiment S3, la distribution CloudFront et la création de fonctions Lambda @ Edge .

Max Desiatov
la source
2

Une autre alternative à l'utilisation de lambda @ edge consiste à utiliser les pages d'erreur de CloudFront. Configurez une réponse d'erreur personnalisée pour envoyer tous les 403 à un fichier spécifique. Ajoutez ensuite du javascript à ce fichier pour ajouter index.html aux URL qui se terminent par un /. Exemple de code:

if ((window.location.href.endsWith("/") && !window.location.href.endsWith(".com/"))) {
    window.location.href = window.location.href + "index.html";
}
else {
    document.write("<Your 403 error message here>");
}
user1333371
la source
1

Je sais que c'est une vieille question, mais je me suis juste battu moi-même. Finalement, mon objectif était moins de définir un fichier par défaut dans un répertoire, et plus d'avoir le résultat final d'un fichier qui était servi sans .htmlà la fin de celui-ci

J'ai fini par supprimer .htmldu nom de fichier et paramétré par programme / manuellement le type mime sur text/html. Ce n'est pas la méthode traditionnelle, mais cela semble fonctionner et satisfait mes exigences pour les jolies URL sans sacrifier les avantages de la formation dans le cloud. Le réglage du type mime est ennuyeux, mais un petit prix à payer pour les avantages à mon avis

whtevn
la source