Deploying a static website in a fault-tolerant configuration in Yandex Cloud
- Solution architecture overview
- Solution overview
- Yandex Object Storage bucket with public access
- Public Yandex Object Storage bucket with Yandex Cloud CDN content delivery
- Yandex Object Storage bucket without public access with Yandex API Gateway and content delivery via Yandex Cloud CDN
- Yandex Object Storage bucket without public access with Yandex Application Load Balancer
This guide provides multiple solutions for deploying a fault-tolerant static website with dynamic elements (such as a registration or application form, etc.) in the Yandex Cloud infrastructure.
These solutions optimize costs and ensure high loading speeds for the website’s end users.
Solution architecture overview
The core infrastructure components in all solutions are Yandex Object Storage and Yandex Virtual Private Cloud. Yandex Cloud CDN ensures fast content delivery to end users. Also, different solutions rely on various combinations of other services within the Yandex Cloud ecosystem, such as Yandex Cloud DNS, Yandex Certificate Manager, Yandex API Gateway, Yandex Application Load Balancer, Yandex Cloud Postbox, and Yandex Cloud Functions.
Fault tolerance is achieved through automatic replication of data stored in Object Storage buckets, as well as optional bucket versioning.
The proposed solutions offer the following benefits:
- You do not need to plan simultaneous fault-tolerant website deployments across multiple availability zones. All services natively provide high availability and fault tolerance.
- Streamlined architecture.
- Lower costs compared to other hosting solution with an option to use
free tierwhich allows you to host low-traffic and low-data projects almost entirely for free.
Automatic infrastructure deployment
This tutorial includes supplemental Terraform configuration examples that allow you to accelerate your cloud infrastructure deployment. These examples provide ready-to-use solutions for common tasks, such as:
- Uploading and updating static website files in Object Storage.
- Creating a Let's Encrypt certificate for the website in Certificate Manager.
- Setting up Yandex Cloud Postbox to send transactional emails.
- Integrating a function from Cloud Functions to send emails and an example of such a function for Node.js
.
You can find the supplemental materials in the
Solution overview
Depending on your website’s requirements, the system architecture of the solutions can vary significantly. However, all hosting options require a custom domain name for the website, a correctly configured DNS for the domain, and an SSL certificate.
This overview describes the following static website hosting solutions in the Yandex Cloud infrastructure:
- Yandex Object Storage bucket with public access
- Public Yandex Object Storage bucket with Yandex Cloud CDN content delivery
- Yandex Object Storage bucket without public access with Yandex API Gateway and content delivery via Yandex Cloud CDN
- Yandex Object Storage bucket without public access with Yandex Application Load Balancer
Yandex Object Storage bucket with public access
Yandex Object Storage buckets feature a dedicated Hosting mode, which is one of the simplest and most affordable ways to host a static website in Yandex Cloud. Smaller projects may also be eligible for a free tier, as an additional benefit. For more information about the Object Storage free tier, see this Yandex Cloud Billing article.
Here is a diagram that illustrates the solution:
Required resources
To host a static website using this solution, you need:
- Object Storage bucket with enabled
Hostingmode. - Domain name and its associated SSL certificate.
- Public DNS zone configured for your domain and a resource record mapping the bucket to your domain name.
Note
To enable access to your website by its domain name, the bucket name and the domain name must match. Here is an example: my.example.com.
For a step-by-step guide on setting up a static website using a public Object Storage bucket with enabled Hosting, see Static website in Yandex Object Storage.
Pros and cons
|
Pros |
Cons |
|
|
Other deployment options can help overcome the above constraints.
Implementation considerations
When deploying a website, consider the following:
-
The type of the DNS resource record mapping a domain name to a bucket depends on the domain level:
- For second-level domains, e.g.,
example.com, create an ANAME resource record. - For third-level domains and higher, e.g.,
my.example.com,my.other.example.com, etc., create a CNAME resource record.
Note
The resource record must always point to the bucket's service domain.
For example, for the
my.example.comdomain in theexample.compublic DNS zone, create amyCNAME record pointing tomy.example.com.website.yandexcloud.net..You must include the trailing dot; omitting it will take your website down.
For more information on how to create a resource record, see Creating a resource record.
- For second-level domains, e.g.,
-
Object Storage is integrated with Certificate Manager, which supports both custom and Let's Encrypt certificates.
When creating a Let's Encrypt certificate, verify your domain ownership. The most reliable and convenient way is DNS verification via a CNAME record. This is a one-time check that requires no further user actions for certificate renewal.
For more on how to pass domain ownership verification, see Verifying domain ownership.
-
The
Hostingmode requires public access to the bucket and unlocks several additional features.Additional bucket features in
Hostingmode-
Requests to the
/path return the file contents specified in the Home page parameter. -
Requests to non-existent objects return the file contents specified in the Error page parameter.
-
Support for request redirection.
-
Service domain natively supports
httpsto handle requests to the main domain. -
Service domain of a bucket in
Hostingmode:website.yandexcloud.net.Note
When the
Hostingmode is disabled, the bucket uses thestorage.yandexcloud.netservice domain.
-
-
If a bucket domain name contains dots,
httpsaccess to the bucket's service domain in<domain_name>.website.yandexcloud.netformat is not possible. The service domain in this format uses a wildcard SSL certificate that only supports fourth-level domains (*.website.yandexcloud.net).To access the bucket's service domain over
httpsin this scenario, use a URL inhttps://website.yandexcloud.net/<domain_name>format.Here is an example:
https://website.yandexcloud.net/my.example.com. -
You can restrict access to the bucket contents using bucket policies. However, these policies may have practical limitations. For example, you can restrict access to bucket contents for specific IP ranges but not for domain names.
For more on how to configure a bucket policy, see Managing a bucket policy.
-
When uploading objects to a bucket, you can set headers to control how your content is processed. For example, the
Content-Typeheader allows you to explicitly define the content’s MIME type.There are more headers you can set for Object Storage bucket objects to customize how browsers process your content.
Common headers
-
Content-Type: Defines the MIME content type of the bucket object. -
Cache-Control: Manages client-side caching and on intermediary caching servers. Customizing client-side caching settings may considerably improve page load speed and reduce traffic consumption.Warning
Use caution when defining caching settings via the
Cache-Controlheader, especially for HTML pages. If a client caches a corrupted page for a long period, the browser will display incorrect information until the cache expires or the user clears it manually. -
Content-Disposition: Instructs the browser to save the object as a local file and sets the default file name. -
Content-Encoding: Indicates the file’s encoding method, e.g.,gzipcompression.
For more on how to set headers for bucket objects, see Troubleshooting.
-
Troubleshooting
Object Storage natively exposes your website content both at its main address, my.example.com, and these Object Storage service domains:
my.example.com.website.yandexcloud.netwebsite.yandexcloud.net/my.example.comstorage.yandexcloud.net/my.example.com
Search engine algorithms treat these addresses as competing URLs, which negatively affects your website's ranking in search results.
For more information about this issue and how to resolve it, see this Yandex Webmaster guide
This behavior may be caused by an incorrect Content-Type metadata header in bucket objects. If you omit the header when uploading an object to the bucket, Object Storage will attempt to auto-detect the content type. This auto-assigned Content-Type header value may be incorrect and cause unexpected behavior.
We recommend that you always explicitly define Content-Type when uploading static website files to the bucket:
- Set the header to
text/htmlso the browser opens the page instead of saving it as a file. - Set the header to
application/javascriptforJavaScriptto run.
Example of using the Yandex Cloud CLI to upload an object with explicitly defined content type.
yc storage s3api put-object \
--bucket my.example.com \
--key index.html \
--body /path/to/my/index.html \
--content-type 'text/html'
Example of a Terraform configuration that automates file uploads and assigns Content-Type to each object based on its extension.
variable "site_source_dir" {
type = string
description = "Site source directory" # Local directory with website files
default = "~/path/to/my/site" # Use the pathexpand() function to handle ~ and other special characters
}
locals {
site_mime_types = jsondecode(file("mime.json")) # File with a table mapping file extensions to Mime types
}
resource "yandex_storage_object" "files" {
for_each = var.site_source_dir == null ? toset([]) : fileset(pathexpand(var.site_source_dir), "**") # Going through all folder files recursively
bucket = "my.example.com"
key = each.key
source = pathexpand("${var.site_source_dir}/${each.key}")
source_hash = filemd5(pathexpand("${var.site_source_dir}/${each.key}")) # Storing file digest in state to trigger file updates when content changes
content_type = lookup(local.site_mime_types, regex("\\.[^.]+$", each.key), null) # Assigning Content-Type dynamically based on the file extension
}
Example of the mime.json contents:
{
".html": "text/html",
".htm": "text/html",
".shtml": "text/html",
".css": "text/css",
".xml": "text/xml",
".gif": "image/gif",
...
}
For information on how to change the content type for a group of objects already uploaded to a bucket, see Fixing issues with incorrect MIME types of objects when uploading them to Object Storage.
Public Yandex Object Storage bucket with Yandex Cloud CDN content delivery
In this solution, a bucket in hosting mode serves as an origin for a Cloud CDN resource. A CNAME resource record maps your website domain name to a special CDN provider domain name assigned to the user cloud folder in Yandex Cloud.
The CDN resource caches end-user requests on its servers and queries the origin bucket only when the requested file is missing from the cache.
The main advantage of CDN is geographical distribution. CDN points of presence are located in major cities in close proximity to your primary content consumers. Content caching and reducing the distance between the client and the CDN server accelerates website loading speed in the end user's browser.
Note
Do not use an ANAME resource record with domain names for content distribution; otherwise, the end user will get a response from a CDN server not linked to the user geolocation. The response will always be the same for all users.
Here is a diagram for a fully static website:
Required resources
To host a static website using this solution, you need:
- Object Storage bucket with enabled
Hostingmode. - Cloud CDN resource.
- Domain name and its associated SSL certificate.
- Configured public DNS zone for the domain and a resource record mapping the CDN resource to your domain name.
For more information on how to create a CDN resource, see Creating a resource.
Pros and cons
|
Pros |
Cons |
|
|
Therefore, if your website's content is cache-friendly, which static websites usually are, and you configure hosting with SEO in mind, you can achieve significant savings in traffic delivery costs, maximize your website's availability to end users, and, as a result, considerably improve the overall customer experience.
Troubleshooting
This solution allows you to restrict access to the bucket and make it accessible only by Cloud CDN hosts. Implementing this restriction prevents search engines from indexing your website via the bucket's internal service domains.
There are several ways to make a bucket accessible only to Cloud CDN hosts:
-
Use access policies to allow bucket access only from Cloud CDN subnets.
The current list of such subnets is publicly
available.Warning
The list of Cloud CDN subnets may change. If using this method to restrict bucket access, make sure to monitor these updates and promptly make changes to the allowing access rules.
For more information, see How to restrict Object Storage bucket access to a specific Cloud CDN IP address range.
-
To avoid having to constantly monitor changes to the Cloud CDN subnet list, you can enable origin shielding and update bucket policies to grant bucket access only to the subnets used for origin shielding:
188.72.110.0/24188.72.111.0/24
The list of these subnets is more stable and less likely to change.
For more on how to configure a bucket policy, see Managing a bucket policy.
This solution enables you to specify different data origins, caching parameters, and other settings within a single domain based on the path of the requested object, including path masks. To implement this behavior, use location rules.
Request processing rules use regular expressions to evaluate the path of each requested object. For each rule, you can specify the following:
- Content origin: Bucket, L7 load balancer, or any server.
- Settings for content storage in CDN and client-side caching settings.
- Rules for request modification and redirection.
- CORS header values.
There are more settings you can configure within request processing rules. For more information, see Location rules Yandex Cloud CDN and Setting up Yandex Cloud CDN location rules.
As a rule, static websites are designed to drive user engagement via targeted calls to action, such as clicking a link or filling out a form.
Using location rules, you can build a fairly complex website that integrates both static and dynamic sections, such as registration forms.
Warning
By default, Cloud CDN blocks the POST, PUT, PATCH, and DELETE HTTP methods for client requests. To remove this restriction, create a request
The traffic load on form data processors is usually uneven and may fluctuate from long idle periods to hundreds of requests per second.
Yandex Cloud Functions provides a convenient tool to process requests under these unpredictable traffic patterns. To invoke functions from website pages, deploy a Yandex API Gateway.
The function is [invoked](*function_call) using a dedicated function URL: https://functions.yandexcloud.net/<function_ID>. However, attempting to call the function directly from a website page in a browser will fail with an error: the browser will block the request because the function URL and the website have different origins.
For the function call to succeed, the function must support preflight
You can implement preflight request handling in Cloud Functions. However, a simpler solution is to use an API gateway with the [x-yc-apigateway-cors](*api_gw_cors) specification extension to invoke the function.
Example of an API gateway specification with the x-yc-apigateway-cors extension.
openapi: "3.0.0"
info:
version: 1.0.0
title: Mailer API
servers:
- url: https://d5dglu90r7y0********.wm******.apigw.yandexcloud.net
paths:
/register:
x-yc-apigateway-cors:
origin:
- https://my.example.com
methods:
- POST
post:
x-yc-apigateway-integration:
type: cloud_functions
function_id: d4edj6bo1uy0********
service_account_id: ajekjkm28y0********
An API gateway created using the specification from the example above allows you to call the d4edj6bo1uy0******** function at https://d5dglu90r7y0********.wm******.apigw.yandexcloud.net/register from website pages hosted in the https://my.example.com domain.
Note
The d4edj6bo1uy0******** function is not public. To call this function, you need a [service account](*service_account) with the functions.functionInvoker [role](*functions_invoker).
Here is a diagram for a static website with dynamic components:
For more information on how to create a function, see Creating a function and Creating a function version.
For more information on how to create an API gateway, see Creating an API gateway.
You can use Yandex Cloud CDN as a global load balancer to implement fault-tolerant scenarios. For example, you can use origin groups to distribute requests across multiple origins hosted by different providers or configure a backup origin for situations when the main origin goes down.
For more on how to use origin groups, see Origin groups.
Yandex Object Storage bucket without public access with Yandex API Gateway and content delivery via Yandex Cloud CDN
In some scenarios, you may need to restrict access to the entire bucket or its part. For example, if your website must contain both a public section, accessible to all visitors, and a private section, accessible only to authorized users.
One way to implement this scenario is to use API Gateway and its extensions.
Here is a diagram that illustrates the solution:
Required resources
To host a static website using this solution, you need:
- Object Storage bucket with
Hostingmode disabled. - API Gateway.
- Cloud CDN resource.
- Domain name and its associated SSL certificate.
- Configured public DNS zone for the domain and a resource record mapping the API gateway service domain to your domain name.
In this solution, you do not need to assign a name to the bucket that matches the domain name. End users will not access the bucket directly, but through an API gateway associated with your domain name.
For more information on how to create an API gateway, see Creating an API gateway.
Pros and cons
|
Pros |
Cons |
|
|
This static website hosting option is not suitable for private corporate portals with access via VPN or Yandex Cloud Interconnect. The API gateway always uses a public IP address and does not have native tools to restrict access by IP address.
Note
To restrict access to API Gateway, you must connect a Yandex Smart Web Security profile and configure access restrictions using basic rules. For a private corporate portal, this hosting solution works better: Yandex Object Storage bucket without public access with Yandex Application Load Balancer.
Implementation considerations
Since you cannot enable the Hosting mode for a non-public bucket, configure request forwarding instead. Make sure to properly configure root page / request processing so that such requests return the contents of your website's main page (typically the index.html file). Additionally, set up an error page to return together with 4xx code responses.
API Gateway configuration example.
openapi: "3.0.0"
info:
version: 1.0.0
title: Mailer API
servers:
- url: https://d5dglu90r7y0********.wm******.apigw.yandexcloud.net
- url: https://my.example.com
paths:
/:
get:
summary: Serve root page
parameters:
- name: file
in: path
required: true
schema:
type: string
x-yc-apigateway-integration:
type: object_storage
bucket: mysecretbucket0x7adfbu
object: index.html
error_object:
object: 404.html
statusCode: 404
service_account_id: ajebg5lgngy0********
/{file+}:
get:
summary: Serve static site content
parameters:
- name: file
in: path
required: true
schema:
type: string
x-yc-apigateway-integration:
type: object_storage
bucket: mysecretbucket0x7adfbu
object: '{file}'
error_object:
object: 404.html
statusCode: 404
service_account_id: ajebg5lgngy0********
The service account with the ajebg5lgngy0******** ID must have a role with read permissions for the mysecretbucket0x7adfbu bucket.
For more information on how to update API gateway settings, see Updating an API gateway.
Troubleshooting
API gateway throughput can handle a significant number of requests per second, but its capacity is still limited. Reaching this threshold will trigger the 429 Too Many Requests gateway error.
To avoid this error, maximize website availability under load, and reduce costs, configure a CDN resource that allows caching of incoming website requests.
Tip
Implement shielding of CDN resource origins to further improve the solution's performance by adding an extra caching layer.
For more information on how to create a CDN resource, see Creating a resource.
As a rule, static websites are designed to drive user engagement via targeted calls to action, such as clicking a link or filling out a form.
The traffic load on these forms is usually uneven and may fluctuate from long idle periods to hundreds of requests per second. These unpredictable traffic patterns make [Yandex Cloud Functions](*function) an ideal tool for processing requests.
The function is [invoked](*function_call) using a dedicated function URL: https://functions.yandexcloud.net/<function_ID>. However, attempting to call the function directly from a website page in a browser will fail with an error: the browser will block the request because the function URL and the website have different origins.
For the function call to succeed, the function must support preflight
You can implement preflight request handling in Cloud Functions. However, a simpler solution is to use an API gateway with the [x-yc-apigateway-cors](*api_gw_cors) specification extension to invoke the function.
Example of an API gateway specification with the x-yc-apigateway-cors extension.
openapi: "3.0.0"
info:
version: 1.0.0
title: Mailer API
servers:
- url: https://d5dglu90r7y0********.wm******.apigw.yandexcloud.net
paths:
/register:
x-yc-apigateway-cors:
origin:
- https://my.example.com
methods:
- POST
post:
x-yc-apigateway-integration:
type: cloud_functions
function_id: d4edj6bo1uy0********
service_account_id: ajekjkm28y0********
An API gateway created using the specification from the example above allows you to call the d4edj6bo1uy0******** function at https://d5dglu90r7y0********.wm******.apigw.yandexcloud.net/register from website pages hosted in the https://my.example.com domain.
Note
The d4edj6bo1uy0******** function is not public. To call this function, you need a [service account](*service_account) with the functions.functionInvoker [role](*functions_invoker).
Here is a diagram for the solution using an API gateway to invoke a function:
For more information on how to create a function, see Creating a function and Creating a function version.
Yandex Object Storage bucket without public access with Yandex Application Load Balancer
To create a static website based on a non-public Object Storage bucket, you can use an L7 load balancer.
L7 load balancers automatically scale as load increases by creating new resource units, which ensures significantly higher performance compared to API Gateway.
Required resources
To host a static website using this solution, you need:
- Object Storage bucket with
Hostingmode disabled. - Application Load Balancer (L7).
- Domain name and its associated SSL certificate.
- Configured public DNS zone for the domain and a resource record mapping the IP address of the L7 load balancer listener to your domain name.
In this solution, you do not need to assign a name to the bucket that matches the domain name. End users will not access the bucket directly, but through an API gateway associated with your domain name.
For more information on how to create an L7 load balancer, see Creating an L7 load balancer.
Pros and cons
|
Pros |
Cons |
|
|
Implementation considerations
When implementing this solution, consider the following Application Load Balancer features:
-
There are fees for load balancer resource units. As the load on your website grows, the number of resource units may increase as per scaling rules, which increases your costs.
Tip
To cut costs for a public website, connect a CDN resource that allows caching of incoming website requests. Implement shielding of CDN resource origins to further improve the solution's performance by adding an extra caching layer.
Note that internal load balancers do not support Yandex Cloud CDN caching.
For more information on how to create a CDN resource, see Creating a resource.
-
Since you cannot enable Hosting on a non-public bucket or a bucket with access via a service connection, make sure to properly configure root page
/request processing on the load balancer side: such requests must return the contents of your website's main page (typicallyindex.htmlfile).For more information on how to configure request processing in a load balancer, see Updating HTTP router settings.
-
L7 load balancer does not create additional (service) domains.
Troubleshooting
To restrict access to bucket data using an L7 load balancer, replace the Referer header with a secret word and configure a bucket policy that allows access to content only if incoming requests contain this specific secret word in the header. You cannot restrict bucket access for Application Load Balancer resource units because Application Load Balancer interacts with the bucket over an internal service network.
Another way to restrict access to bucket data is to use buckets with access via a service connection. Service connections allow you to create buckets isolated from the internet and accessible only via internal Yandex Virtual Private Cloud IP addresses. Choose this method when the data you store requires enhanced security.
For information on how to configure a bucket policy, see Managing a bucket policy.
As a rule, static websites are designed to drive user engagement via targeted calls to action, such as clicking a link or filling out a form.
The traffic load on these forms is usually uneven and may fluctuate from long idle periods to hundreds of requests per second. These unpredictable traffic patterns make [Yandex Cloud Functions](*function) an ideal tool for processing requests.
The function is invoked using a dedicated function URL: https://functions.yandexcloud.net/<function_ID>. However, attempting to call the function directly from a website page in a browser will fail with an error: the browser will block the request because the function URL and the website have different origins.
For the function call to succeed, the function must support preflight
You can implement preflight request processing in Cloud Functions.
For more information on how to create a function, see Creating a function and Creating a function version.