Как перенаправить 301 (HTTP на HTTPS) && (с www на не-www) для одного домена, используя S3 и Cloudfront? - PullRequest
0 голосов
/ 10 октября 2018

Я размещаю статический сайт (исключительно html / css) на AWS S3 с дистрибутивом CloudFront.У меня нет проблем с настройкой только CloudFront для перенаправления HTTP на HTTPS.И при этом у меня нет проблемы только с S3, перенаправляющим www на поддомен не-www (голый).

Проблема возникает, когда я пытаюсь перенаправить весь HTTP-трафик на HTTPS и одновременно перенаправить все поддомены www на не-www.

Это просто не работает.И мне не удалось найти решение этой проблемы, и я искал несколько месяцев.Может показаться, что у StackOverflow есть ответ, но я говорю вам, что нет.Либо их решение зашло в тупик, либо решение для более старого пользовательского интерфейса AWS, который не совсем соответствует нынешнему.

Лучшее, что я смог придумать, - это перенаправление HTMLот www до non-www, но это не идеально с точки зрения SEO и удобства обслуживания.

Какое решение лучше для этой конфигурации?

Ответы [ 2 ]

0 голосов
/ 14 октября 2018

Завершая другой ответ здесь , используя Lambda @ Edge, я понял, что существует значительно более простое решение, использующее только один дистрибутив CloudFront и три (описанные ниже) сегмента S3.

Это решение имеет больше ограничений, но оно имеет меньше движущихся частей и требует меньших затрат на внедрение и использование.

Вот ограничения:

  • Вы должны использовать веб-сайт S3функция хостинга (должна быть предоставлена, поскольку речь идет о размещении контента и выполнении перенаправлений)
  • Все сегменты должны находиться в одном регионе AWS
  • Первые два сегмента должны называться точно то же самое, что и имена хостов, которые вы хотите обработать - например, вам нужен контейнер с именем example.com и контейнер с именем www.example.com.
  • Вам также необходимо создать контейнер с именем точносоответствует имени хоста, назначенному для дистрибутива CloudFront, например dzczcexample.cloudfront.net, и этот сегмент также должен находиться в том же регионе, что и два других.

Настройка ClouИсходное доменное имя дистрибутива dFront для указания на ваш основной сегмент контента с использованием конечной точки хостинга веб-сайта, например, example.com.s3-website.us-east-2.amazonaws.com.

Настройте параметры альтернативного имени домена для example.com и www.example.com.

Белый список заголовок Host для пересылки к источнику.Этот параметр использует преимущество того факта, что когда S3 не распознает входящий заголовок HTTP Host как принадлежащий S3, тогда ...

контейнер для запроса - это значение в нижнем регистрезаголовка Host, а ключ для запроса - Request-URI.

https://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html

Уммм ... идеально!Это именно то, что нам нужно - и это дает нам возможность передавать запросы нескольким сегментам в одном регионе S3 через один дистрибутив CloudFront на основе того, что запрашивает браузер ... потому что с помощью этой настройки мы можемразделить логику:

  • Имя домена происхождения используется только для маршрутизации запроса от края CloudFront к правильному региону S3, затем
  • в белый список *Заголовок 1051 * используется, когда запрос поступает на S3 для , выбирая, какой сегмент обрабатывает запрос .

(Вот почему все сегменты должны находиться в одном регионе, так какупомянутый выше. В противном случае запрос будет доставлен в область «основного» сегмента, и этот регион отклонит его как ошибочно направленный, если идентифицированный сегмент находится в другом регионе.)

С этой конфигурацией на местевы обнаружите, что example.com запросы обрабатываются корзиной example.com, а www.example.com запросы обрабатываются корзиной www.example.com, что означает все, что вам нужносейчас нужно настроить сегменты как вам нужно.

Но есть еще один важный шаг. Вам абсолютно необходимо создать сегмент, названный в соответствии с назначенным по умолчанию доменным именем вашего дистрибутива CloudFront (например, d111jozxyqk.cloudfront.net), чтобы избежать создания эксплуатируемого сценария.Это не уязвимость безопасности, это биллинг.То, как вы конфигурируете этот сегмент, не имеет большого значения, но важно, чтобы он был вашим владельцем, чтобы никто другой не мог его создать.Зачем?Поскольку при такой конфигурации запросы, отправленные непосредственно на доменное имя по умолчанию вашего дистрибутива CloudFront (не на ваши пользовательские домены), приведут к тому, что S3 вернет No Such Bucket error для этого имени сегмента .Если бы кто-то другой обнаружил вашу настройку, он мог бы создать эту корзину, вы бы заплатили за весь их трафик данных через дистрибутив CloudFront.Создайте корзину и оставьте ее пустой (чтобы возвращалась ошибка) или настройте ее для перенаправления на основной веб-сайт.

0 голосов
/ 14 октября 2018

Как я уже упоминал в Поддержка перенаправления URL-адресов HTTPS с помощью одного дистрибутива CloudFront , простое и понятное решение включает в себя два сегмента и два дистрибутива CloudFront - один для www и один для чистого домена.Я очень скептически отношусь к тому, что это окажет негативное влияние на SEO.

Однако этот ответ предшествует введению расширения CloudFront Lambda @ Edge , которое предлагает другое решение, поскольку оно позволяет вамзапускать функцию Javascript Lambda в определенных точках во время обработки запроса CloudFront, проверять запрос и, возможно, изменять его или иным образом реагировать на него.

В документации есть несколько примеров , новсе они очень минималистичны, так что вот полный рабочий пример с большим количеством комментариев, чем с реальным кодом, объясняющий, что именно он делает и как он это делает.

Эта функция - сконфигурированная как триггер исходного запроса -будет срабатывать каждый раз, когда будет отсутствовать кэш, и проверять заголовок Host, отправленный браузером, чтобы выяснить, следует ли разрешить запрос или он должен быть перенаправлен без фактической отправки запроса на S3.При попадании в кэш эта функция не срабатывает, поскольку CloudFront уже имеет кэшированное содержимое.

Любое другое доменное имя, связанное с дистрибутивом CloudFront, будет перенаправлено на «реальное» доменное имя вашего сайта, как настроено втело функции.При желании он также вернет сгенерированный ответ 404, если кто-то напрямую получит доступ к имени хоста по умолчанию *.cloudfront.net вашего дистрибутива.

Может быть, вам интересно, как кэш одного дистрибутива CloudFront может различать контент для example.com/some-path иwww.example.com/some-path и кэшируйте их по отдельности, но ответ таков: он может, и он делает , если вы настроите его соответствующим образом для этой настройки - это означает, что он будет кэшироваться на основе выбранных заголовков запросов - в частности, заголовок Host.

Обычно включение этой конфигурации не совсем совместимо с S3, но здесь это работает, потому что функция Lambda также устанавливает заголовок Host обратно на то, что ожидает S3.Обратите внимание, что вам нужно настроить исходное доменное имя - конечную точку размещения веб-сайта вашего сегмента - встроенное в коде.

При такой конфигурации вам нужен только один блок, а имя блока не соответствуетНеобходимо сопоставить любое из доменных имен.Вы можете использовать любой сегмент, который вам нужен ... но вам нужно использовать конечную точку хостинга веб-сайта для сегмента, чтобы CloudFront рассматривал его как пользовательский источник.Создание «источника S3» с использованием конечной точки REST для сегмента не будет работать.

'use strict';

// if an incoming request is for a domain name other than the canonical
// (official) hostname for the site, this Lambda@Edge trigger
// will redirect the request back to the official site, subject to the
// configuration parameters below.

// this trigger must be deployed as an Origin Request trigger.

// in the CloudFront Cache Behavior settings, the Host header must be
// whitelisted for forwarding, in order for this function to work as intended;
// this is an artifact of the way the Lambda@Edge interface interacts with the
// CloudFront cache key mechanism -- we can't react to what we can't see,
// and if it isn't part of the cache key, CloudFront won't expose it.

// specify the official hostname of the site; requests to this domain will
// be passed through; others will redirect to it...

const canonical_domain_name = 'example.com'; 

// ...but note that every CloudFront distribution has a default *.cloudfront.net
// hostname that  can't be disabled; you may not want this hostname to do
// anything at all, including redirect; set this parameter to true if you
// want to to return 404 for the default hostname; see the render_reject()
// function to customize the behavior further.

const reject_default_hostname = false; 

// the "origin" is the server that provides your content; this is configured
// in the distribution and selected in the Cache Behavior settings, but
// that information needs to be provided here, so that we can modify
// successful requests to match what the destination expects.

const origin_domain_name = 'example-bucket.s3-website.us-east-2.amazonaws.com';

// http status code for redirects; you may want 302 or 307 for testing,
// and 301 or 308 for production; note that this is a string, not a number.

const redirect_http_status_code = '302';

// for generated redirects, we can also set a cache control header; you'll need
// to ensure you format this correctly, since the code below does not validate
// the syntax; here, max-age is how long the browser should cache redirects, 
// while s-maxage tells CloudFront how long to potentially cache them;
// higher values should result in less traffic and potentially lower costs;
// set to empty string or null if you don't want to set a value.

const redirect_cache_control = 'max-age=300, s-maxage=86400';

// set false to drop the query string on redirects; true to preserve

const redirect_preserve_querystring = true;

// set false to change the path to '/' on redirects; true to preserve

const redirect_preserve_path = true;

// end of configuration

// the URL in the generated redirect will always use https unless you
// configure whitelisting of CloudFront-Forwarded-Proto, in which case we
// will use that value; if you want to send http to https, use the
// Viewer Protocol Policy settings in the CloudFront cache behavior.


exports.handler = (event, context, callback) => {

    // extract the CloudFront object from the trigger event    
    const cf = event.Records[0].cf;

    // extract the request object
    const request = cf.request;

    // extract the HTTP Host header
    const host = request.headers.host[0].value;

    // check whether the host header matches the canonical value; if so,
    // set the host header to what the origin expects, and return control
    // to CloudFront

    if(host === canonical_domain_name)
    {
        request.headers.host[0].value = origin_domain_name;
        return callback(null, request);
    }

    // check for rejection

    if (reject_default_hostname && host.endsWith('.cloudfront.net'))
    {
        return render_reject(cf, callback);
    }

    // if neither 'return' above has been invoked, then we need to generate a redirect.

    const proto = (request.headers['cloudfront-forwarded-proto'] || [{ value: 'https' }])[0].value;

    const path = redirect_preserve_path ? request.uri : '/';

    const query = redirect_preserve_querystring && (request.querystring != '') ? ('?' + request.querystring) : '';

    const location = proto + '://' + canonical_domain_name + path + query;

    // build a response object to redirect the browser.

    const response = {

        status: redirect_http_status_code,
        headers: {
            'location': [ { key: 'Location', value: location } ],
        },    
        body: '',

    };

    // add the cache control header, if configured

    if(redirect_cache_control)
    {
        response.headers['cache-control'] = [{ key: 'Cache-Control', value: redirect_cache_control }];
    }

    // return the response object, preventing the request from being sent to
    // the origin server

    return callback(null, response);

};

function render_reject(cf, callback) {
    // only invoked if the request is for *.cloudfront.net and you set
    // reject_default_hostname to true; here, we generate a very simple
    // response, text/plain, with a 404 error.  This can be customized to HTML
    // or XML, etc., according to your local practices, but be sure you properly
    // escape the request URI, since it is untrusted data and could lead to an
    // XSS injection otherwise; no similar vulnerability exists with plain text.

    const body_text = `The requested URL '${cf.request.uri}' does not exist ` +
                      'on this server, or access is not enabled via the ' +
                      `${ cf.request.headers.host[0].value } endpoint.\r\n`;

    // generate a response; you may want to customize this; note that
    // Lambda@Edge is strict with regard to the way headers are specified;
    // the outer keys are lowercase, the inner keys can be mixed.

    const response = {
        status: '404',
        headers: {
            'cache-control': [{ key: 'Cache-Control', value: 'no-cache, s-maxage=86400' }],
            'content-type':  [{ key: 'Content-Type',  value: 'text/plain' }],
        },
        body: body_text,
    };

    return callback(null, response);
}

// eof
...