As you may have noted, so far, we only have a URL generated by AWS. It is not friendly nor does it use our custom SSL certificate, so we may want to replace it with a custom domain and an SSL certificate. The API Gateway supports it out of the box, but it is very poor support, and first, it needs the certificate to be copies and pasted manually, which is not the best way of enabling SSL, especially for big corporates.
AWS is offering a better solution to solve all these issues and cover the lack of some features of API Gateway. CloudFront is the CDN offering of AWS, and it supports many features. Here, we will set up a CloudFront distribution, which will proxy API Gateway and we will gain many features, such as the following:
- Free SSL support via ACM (Amazon Certificate Manager)
- HTTP/2 and IPv6 support
- GZIP compression
- HTTP caching
CloudFront configuration is not too complicated. We can start with the following snippet and check out line by line what is going on:
"CloudformationDistribution": {
"Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { "Enabled": "true", "HttpVersion": "http2", "Origins": [ { "DomainName": { "Fn::Sub": "${RestApi}.execute-api.
${AWS::Region}.amazonaws.com" }, "OriginPath": "/production", "Id": "APIGATEWAY", "CustomOriginConfig": { "OriginProtocolPolicy": "https-only" } } ], "DefaultCacheBehavior": { "TargetOriginId": "APIGATEWAY", "Compress": true, "AllowedMethods": [ "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT" ], "ForwardedValues": { "QueryString": "true", "Cookies": { "Forward": "none" }, "Headers": [ "Accept", "Content-Type", "Authorization" ] }, "DefaultTTL": 0, "MaxTTL": 0, "MinTTL": 0, "ViewerProtocolPolicy": "redirect-to-https" } } } }
First, we define the resource as the AWS::CloudFront::Distribution type and then we get a couple of configuration options.
We start with setting Enabled to true which. HttpVersion is used to enable the recently brought HTTP/2 support. This support is free and there is no reason not to enable it since it is also backward-compatible with clients that do not support it. After that, we start defining an origin to be proxied, which is the API Gateway in our case. It is possible to define more than one origin and route among them using different paths.
For now, we have only one origin, but in the following chapters, we will define an S3 bucket as another origin to show profile pictures of our users. With DomainName, we construct the domain name of our API Gateway endpoint using the Fn::Sub function and variables from other resources. OriginPath is very useful in our case: we set it to /production in order to omit this unnecessary part of the URL. Id is a custom name for our origin. In CustomOriginConfig, we define using which protocol CloudFront will use to access the backend. API Gateway supports only HTTPS, so we pick the https-only option here. After defining the origin, we now define the default cache behavior. CloudFront can act differently for different paths. Say, if you have a directory that has only static files, you can create a different cache configuration for that path. If CloudFront cannot find any special cache configuration matching with that path, it uses the default cache behavior. In the DefaultCacheBehavior section, we first declare that all requests should be fulfilled using the APIGATEWAY origin. Compress is an option to enable the GZIP compression. It is a must to have this compression, and unfortunately, API Gateway does not provide it, so we can overcome this problem by enabling this option, so our clients will get compressed results. AllowedMethods declares which HTTP methods we are supporting. For a typical REST API, all the HTTP methods available make sense, so we enable all of them. ForwardedValues is a important option. Here, we define which HTTP request values we want to forward to the backend. For now, we do not enable caching on the CloudFront side, and we will do that later when we build some real endpoints and decide that it is appropriate to cache results. However, at this stage, we should know that CloudFront creates the cache key using the forwarded values. For this configuration, we forward all the query strings and forward none of the cookies because for a REST API, cookies do not make any sense. We only forward the Accept, Content-Type, and Authorization headers. If we had enabled caching, for each client with a different Accept, Content-Type, and Authorization header, the cached response would be different. If you want more fine-grained configuration, for every endpoint, you can create a new cache behavior configuration and define exactly which query parameters and headers to pass to the backend. At this stage, for the sake of simplicity, we leave this as a generic configuration.
CloudFront configuration is almost complete with a default CloudFront domain. At this stage, we can deploy the application and wait until the CloudFront configuration is deployed. This time, deployment will take more time than usual because CloudFront has a lot of machines all over the world and the propagation of a new configuration takes time. Gradle will probably time out, but no worries; the deployment of the CloudFormation stack will continue and you can observe it on the AWS console.
When deployment finishes, you can navigate to the CloudFront section of the AWS Console. There, you can see your new CDN distribution and read the domain name assigned automatically by CloudFront. You can try to send a request to the CloudFront distribution now:
$ curl https://d3foeb7gudycc2.cloudfront.net/test?
value=hello+world {"value":"hello world"}
Note that the URL you have will be different than the preceding command.
We are not finished yet because we have still not enabled the custom domain.