AWS CloudFront CDN
Deploys an AWS CloudFront distribution (CDN) that can be used to proxy requests to your upstream origins.
This module performs several functions in addition to providing sensible defaults for the CDN:
- Establishes the CDN TLS certificates
- Deploys the DNS records for the CDN
- Provides pre-packaged edge function behaviors (e.g., URL rewrite rules)
- Configures request logging (optional)
Limitations
-
The domain names used for the CDN must be served by Route53 zones in the AWS account within which this module is deployed. This zone must be deployed before using this module because this module automatically configures all the necessary DNS records.
-
Your origin servers must be identified by domain name (not IP address) and be able to serve HTTPS traffic from their domain (i.e., have valid TLS certificates).
Usage
Overview
Conceptually, a CloudFront distribution is a set of servers distributed all over the world that collectively comprise a content distribution network (CDN). These servers are called points-of-presence (PoPs). PoPs serve several purposes:
- Enhancing TLS performance by speeding up the initial connection between the client and server (reference)
- Improving overall performance and reducing load on servers by caching responses from origin servers
- Running edge computations (CloudFront functions)
Unlike traditional servers where DNS records such as example.com
route
to a single set of servers, CloudFront works with AWS's DNS servers to ensure that clients are always routed
to the nearest PoP.
The set of domains that get routed this way is defined by the domains
input to this module. How traffic gets
routed from the PoP to your origin servers is configured by origin_configs
.
Using the Global Provider
terraform {
required_providers {
...
aws = {
source = "hashicorp/aws"
version = "5.70.0"
configuration_aliases = [aws.global]
}
...
}
}
module "cdn" {
source = "${var.pf_module_source}aws_cdn${var.pf_module_ref}"
# Since the CDN operates globally, you must use the global
# provider as follows:
providers = {
aws.global = aws.global
}
name = "example"
domains = [...]
origin_configs = [...]
}
Configuring Origin Routing
Overview
You can specify multiple upstream origins via origin_configs
. Which origin traffic gets routed to
is controlled via path_prefix
. Each origin must have a unique path_prefix
.
Traffic that matches a given path_prefix
will:
-
The CDN will check to see if the request path matches any of the
path_match_behavior
keys (e.g.,*.jpg
). If so, the rules from that behavior configuration will be applied. See below. -
If no
path_match_behavior
keys are matched (or none are provided), thedefault_cache_behavior
configuration will take effect.
The cache "behavior" for both default_cache_behavior
and path_match_behavior
works as follows:
-
Any global redirect rules (see below) will take effect. Otherwise, the request will be processed.
-
If
caching_enabled
istrue
, the request's HTTP method is incached_methods
, and the request has a cached response, then a cached response will be immediately returned. -
If no cached response is found and the request HTTP method is in
allowed_methods
, the request be forwarded to theorigin_domain
in its original form (including the originalHost
header) except for anything blocked bycookies_not_forwarded
,headers_not_forwarded
, andquery_strings_forwarded
. -
When a response is received, CloudFront will automatically compress responses if
compression_enabled
istrue
and the client accepts either the Gzip or Brotli compression formats (via theAccept-Encoding
HTTP header). -
After optional compression, the request will be cached if
caching_enabled
istrue
and the request's HTTP method is incached_methods
. The cache key is determined bycookies_in_cache_key
,headers_in_cache_key
, andquery_strings_in_cache_key
(the HTTP method and path are always included in the key). 2How long the request will stay cached depends on the following values and the response's Cache-Control and Expires headers:
min_ttl
: The minimum amount of time in seconds that CloudFront will keep responses in the cache before revalidating them with the origin server.default_ttl
: The amount of time in seconds that CloudFront will keep responses in the cache only when the response does not supplyCache-Control
orExpires
headers.max_ttl
: The maximum amount of time in seconds that Cloudfront will keep responses in the cache before revalidating them with the origin server regardless of the response'sCache-Control
orExpires
headers.Cache-Control
response headers: CloudFront respects themax-age
,no-cache
,no-store
,private
,stale-while-revalidate
andstale-if-error
directives.Expires
response headers: Cloudfront respects this header butCache-Control
should be preferred due to its increased specificity.
For a much more detailed breakdown of CloudFront caching, see AWS's documentation.
-
The response will be sent to the client.
No Origin Matches
If the distribution receives a request but does not match any path_prefix
, CloudFront will return an HTTP 404
status code.
Redirect Rules
We provide an input, redirect_rules
, that allows you to specify redirects that will be applied before requests
get sent to your origin servers.
For example:
redirect_rules = [{
source = "https?://example.com(/.*)"
target = "https://new.example.com$1"
}]
The above rule would redirect a request for http://example.com/some/resource
to https://new.example.com/some/rseource
.
CORS
This module allows CORS header handling to happen inside CloudFront rather than by your origin servers.
To enable this functionality, set cors_enabled
to true
.
When this is enabled, CloudFront will respond to all OPTIONS
preflight requests without forwarding any requests to the
origin. Additionally, CloudFront will ensure all responses returned from the origin have the appropriate CORS
headers set before forwarding the response to the client.
You can customize the CORS behavior with the following inputs:
cors_max_age_seconds
: Sets theAccess-Control-Max-Age
response header.cors_allowed_headers
: Sets theAccess-Control-Allow-Headers
response header.cors_allowed_methods
: Sets theAccess-Control-Allow-Methods
response header.
By default, the only allowed origins will be from the domains
input. If you want to allow additional origins,
you must set cors_additional_allowed_origins
. Adding "*"
to this input will allow all origins.
S3 Origins
If you want to use an S3 bucket as the origin for this CloudFront distribution, you should use our aws_s3_public_website module.
Number of PoPs
The number of PoPs is configured by CloudFront's price class. If you
are serving to non-NA clients, you may want to change the default price_class
for this module.
Origin Shield
By default, caching is performed independently at each PoP. That means if content is cached in one PoP, other PoPs will still have to make requests to the origin server to retrieve that content.
If you enable Origin Shield, an additional, unified caching layer is introduced before your origin servers. That means that if content is cached for one PoP, it will be cached for all PoPs.
Be aware that this incurs additional charges that can be significant depending on your workload, and it is best used for workloads that serve mostly static content. See this documentation.
Security
Restricting Client Geographies
You can control what countries clients can connect to your distribution from by setting geo_restriction_type
and geo_restriction_list
. For example, if you set geo_restriction_type
to "whitelist"
and geo_restriction_list
to ["US"]
, then only clients can connect from the United States. If you set geo_restriction_type
to "blacklist"
,
then client can connect from anywhere but the United States.
Logging
Logging of requests can be enabled by setting logging_enabled
to true
.
This only configures standard logging,
not the more expensive real-time logs.
As a result, logs may take up to an hour to be created in the S3 bucket (outputs.log_bucket
).
Invalidating the Cache
If you need to manually purge the distribution of all cached responses, you can do so by following this documentation.
Providers
The following providers are needed by this module:
Required Inputs
The following input variables are required:
domains
Description: A list of domains to use for the CDN
Type: list(string)
name
Description: The name of the CDN that will get created
Type: string
origin_configs
Description: A list of configuration settings for communicating with the upstream origins
Type:
list(object({
origin_id = optional(string) # A globally unique identifier for this origin (will be automatically computed if not provided)
origin_domain = string # The domain name of the ingress origin
path_prefix = optional(string, "/") # Only traffic with this HTTP path prefix will be routed to the indicated origin
extra_origin_headers = optional(map(string), {}) # Headers sent from the CDN to the origin
origin_access_control_id = optional(string, null) # The OAC id to use for accessing private origins
# The default behavior of the CDN before routing requests to this origin
default_cache_behavior = optional(object({
caching_enabled = optional(bool, true) # Whether the CDN should cache responses from the origin (overrides all other caching settings)
allowed_methods = optional(list(string), ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]) # What HTTP methods are allowed
cached_methods = optional(list(string), ["GET", "HEAD"]) # What HTTP methods will be cached
min_ttl = optional(number, 0) # Minimum cache time
default_ttl = optional(number, 86400) # Default cache time
max_ttl = optional(number, 31536000) # Maximum cache time
cookies_in_cache_key = optional(list(string), ["*"]) # Which cookies will be included in the cache key (Providing "*" means ALL cookies)
headers_in_cache_key = optional(list(string), [ # Which headers will be included in the cache key
"Authorization",
"Origin",
"x-http-method-override",
"x-http-method",
"x-method-override",
"x-forwarded-host",
"x-host",
"x-original-url",
"x-rewrite-url",
"forwarded"
])
query_strings_in_cache_key = optional(list(string), ["*"]) # Which query strings will be included in the cache key (Providing "*" means ALL query strings)
cookies_not_forwarded = optional(list(string), []) # Which cookies will NOT be forwarded to the ingress from the CDN
headers_not_forwarded = optional(list(string), []) # Which headers will NOT be forwarded to the ingress from CDN
query_strings_not_forwarded = optional(list(string), []) # Which query strings will NOT be forwarded to the ingress from the CDN
compression_enabled = optional(bool, true) # Whether the CDN performs compression on your assets
viewer_protocol_policy = optional(string, "redirect-to-https") # What should happen based on the client protocol (HTTP vs HTTPS). One of: allow-all, https-only, redirect-to-https
}))
# Similar to default_cache_behavior but allows you to specific specific rules for certain path patterns
# The keys for this map are the path patterns (e.g., "*.jpg")
# Path patterns will automatically be prefixed with the path_prefix value, so it can be omitted
path_match_behavior = optional(map(object({
caching_enabled = optional(bool, true)
allowed_methods = optional(list(string), ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"])
cached_methods = optional(list(string), ["GET", "HEAD"])
min_ttl = optional(number, 0)
default_ttl = optional(number, 86400)
max_ttl = optional(number, 31536000)
cookies_in_cache_key = optional(list(string), ["*"])
headers_in_cache_key = optional(list(string), [
"Authorization",
"Origin",
"x-http-method-override",
"x-http-method",
"x-method-override",
"x-forwarded-host",
"x-host",
"x-original-url",
"x-rewrite-url",
"forwarded"
])
query_strings_in_cache_key = optional(list(string), ["*"])
cookies_not_forwarded = optional(list(string), [])
headers_not_forwarded = optional(list(string), [])
query_strings_not_forwarded = optional(list(string), [])
compression_enabled = optional(bool, true)
viewer_protocol_policy = optional(string, "redirect-to-https")
})), {})
}))
Optional Inputs
The following input variables are optional (have default values):
cors_additional_allowed_origins
Description: Specifies which origins are allowed besides the domain name specified. Use '*' to allow any origin.
Type: list(string)
Default: []
cors_allowed_headers
Description: Specifies which headers are allowed for CORS requests.
Type: list(string)
Default:
[
"Content-Length"
]
cors_allowed_methods
Description: Specifies which methods are allowed. Can be GET, PUT, POST, DELETE or HEAD.
Type: list(string)
Default:
[
"GET",
"HEAD"
]
cors_enabled
Description: True if the CloudFront distribution should handle adding CORS headers instead of the origin.
Type: bool
Default: false
cors_max_age_seconds
Description: Time in seconds that the browser can cache the response for a preflight CORS request.
Type: number
Default: 3600
description
Description: A description for this CDN
Type: string
Default: null
geo_restriction_list
Description: A list of ISO 3166 country codes for the geographic restriction list (works for both whitelist and blacklist)
Type: list(string)
Default: []
geo_restriction_type
Description: What type of geographic restrictions to you want to apply to CDN clients. Must be one of: none, blacklist, whitelist.
Type: string
Default: "none"
logging_cookies_enabled
Description: Whether cookies should be included in the request logs
Type: bool
Default: false
logging_enabled
Description: Whether request logging should be enabled for the CloudFront distribution
Type: bool
Default: false
logging_expire_after_days
Description: The number of days after which logs will be deleted. (0 to disable)
Type: number
Default: 0
origin_shield_enabled
Description: Whether origin shield should be enabled for the CloudFront distribution
Type: bool
Default: false
price_class
Description: The price class for the CDN. Must be one of: PriceClass_All, PriceClass_200, PriceClass_100.
Type: string
Default: "PriceClass_100"
redirect_rules
Description: A list of redirect rules that the ingress will match against before sending requests to the upstreams
Type:
list(object({
source = string # A regex string for matching the entire request url (^https://domain.com(/.*)?$)
target = string # The redirect target (can use numbered capture groups from the source - https://domain2.com/$1)
permanent = optional(bool, false) # If true will issue a 301 redirect; otherwise, will use 302
}))
Default: []
Outputs
The following outputs are exported:
distribution_id
Description: The ID of the CloudFront distribution
logging_bucket_name
Description: The name of the log bucket for CloudFront access logs
Maintainer Notes
No notes.
Footnotes
-
We default to
redirect-to-https
for backwards-compatibility, but it is more secure to usehttps-only
. See the documentation for HTTP Strict Transport Security. ↩ -
The more values you specify for the cache key, the lower your hit ratio will be, so tune this carefully. By default, we include all cookies and query strings in the cache key to ensure that responses are not unintentionally shared across clients. You will likely want to loosen these settings for your specific scenario. For more information on tuning the cache key see this documentation. ↩