DEV Community

Cover image for Protect Your On-Premises Web Application with AWS WAF: A Step-by-Step Guide
Cristhian Becerra
Cristhian Becerra

Posted on

Protect Your On-Premises Web Application with AWS WAF: A Step-by-Step Guide

TL;DR: This article will guide you through implementing AWS WAF for your web applications hosted on On-Premises servers. I’ll show step by step how to create SSL/TLS certificates, deploy a CloudFront distribution protected by WAF, and configure the necessary DNS routing for this implementation, all aligned with the Security pillar of the AWS Well-Architected Framework. 🧱


Table of Contents:


Introduction

If you’ve heard about AWS WAF, you probably know that it’s a layer 7 firewall service that allows you to associate a Web Application Firewall directly with AWS resources such as Application Load Balancer, API Gateway, CloudFront Distributions, among others. However, it cannot be directly associated with an EC2 instance, specific IP addresses, or a particular ENI network interface; it can only be linked to certain AWS services.

One day I received a request from a client who wanted to implement AWS WAF for a web application hosted on an On-Premises server exposed through a public IP address. The client clarified that they did not plan to migrate the server to the cloud; the server would remain On-Premises, but they needed WAF protection and wanted to know if it was possible to achieve this from AWS. 🤔

With a bit of creativity, I came up with a solution: deploying a CloudFront distribution protected by AWS WAF, with the origin being the On-Premises server. After generating a few SSL/TLS certificates, modifying a couple of DNS records, and slightly adjusting the configuration of the local application, I managed to implement this solution with excellent results and a completely satisfied client. 😎

In this article, I’ll explain why you should implement AWS WAF for your On-Premises web application, how I did it, and how you could do it too.

Why Implement AWS WAF in an On-Premises Web Application

If you have an On-Premises web application and need to protect it with a Web Application Firewall (WAF) but don’t want to deploy and manage a local WAF solution manually, an excellent alternative is to implement AWS WAF.

AWS WAF is a fully managed service by Amazon Web Services, which means you only need to define which rules to apply and which resource to protect. The service includes managed rules that analyze traffic for patterns of common vulnerabilities, and AWS takes care of maintaining and updating those rules for you. In addition, you can create custom rules, whether stateful or stateless, allow or block sets of IP addresses, apply geographical restrictions, create rules based on regular expressions (regex), among many other options. 🤩

With AWS WAF, you can provide your On-Premises web application with the protection of a modern firewall without the need to provision or manually manage a local WAF infrastructure. And if time is a critical factor, configuring AWS WAF is likely to be much faster than any other traditional alternative. 😏

Whether you’re looking to strengthen the security of your web applications or comply with regulatory requirements, it’s important to know how to implement AWS WAF in an On-Premises web application.

What You Are Going to Implement

This guide assumes that you have an On-Premises server hosting a web application exposed on port 443, with a public IP address protected by HTTPS through an SSL/TLS certificate, and accessible from a domain (for example, www) via an A-type DNS record.

Starting from this point, we will perform the following:

  • Create two SSL/TLS certificates: one to configure in CloudFront and another to install on the On-Premises server.
  • Install the corresponding SSL/TLS certificate on the On-Premises server.
  • Create a CloudFront distribution, configured with its corresponding SSL/TLS certificate, whose origin will be a domain pointing to the On-Premises server.
  • Configure a CNAME-type DNS record to route all domain traffic to CloudFront.
  • Configure an A-type DNS record to route traffic from CloudFront to the On-Premises server.
  • Configure the On-Premises application to accept traffic only from CloudFront.

By the end of this implementation, we will have a resolution and redirection chain similar to the following flow:

  1. Domain: www.mydomain.com
  2. DNS of the CloudFront distribution
  3. CloudFront distribution protected by AWS WAF
  4. Domain: origin.mydomain.com
  5. Public IP address of the On-Premises server

Architecture

Prerequisites

For this AWS WAF implementation, the following prerequisites are required:

  • Access to an AWS account.
  • IAM permissions in the AWS account to use Amazon CloudFront, AWS WAF, Amazon Route 53, and AWS ACM.
  • Administrative access to the Authoritative DNS Provider (Cloudflare or Amazon Route 53).
  • Access to an On-Premises server that hosts a web application exposed on port 443, with a public IP address.
    • Access to create SSL/TLS certificates issued by a public Certificate Authority (CA), for example, the same CA that issued the certificate currently installed on the On-Premises server.
    • Permissions to install the certificate on the On-Premises server.
    • Permissions to edit the configuration of the On-Premises application.

Implementation Steps

The following sections provide a step-by-step guide for implementing AWS WAF on an On-Premises server, showing how each of the components integrates, such as the SSL/TLS certificates from ACM and a public CA, the DNS records in Amazon Route 53, the CloudFront distribution and the AWS WAF web ACL, in order to implement WAF on our On-Premises web application and ensure that all traffic is routed securely through AWS.

Create the SSL/TLS Certificates

Create and install the certificate for the On-Premises server

To begin this implementation, we need to create a new certificate for the On-Premises server. As mentioned earlier, it is assumed that communication to your server was already protected via HTTPS, using an SSL/TLS certificate previously installed for the main hostname, for example, www.mydomain.com.

1. Following the same method you originally used to generate the previous certificate with a public CA, now obtain a valid certificate for origin.mydomain.com, either with the same public CA or another one of your choice.

2. Then, install the new SSL/TLS certificate in the same path where the previous certificate was located. Make sure the installation has been completed successfully.

3. To validate that the certificate installation is correct, you can use online tools such as sslshopper.com or sslchecker.com to verify the hostname origin.mydomain.com. The certificate must be valid for that hostname and not expired.

Create the certificate for CloudFront

1. In the AWS console, navigate to the AWS Certificate Manager service and click the Request a Certificate button.

AWS Certificate Manager

2. In the Certificate Type section, make sure Request a public certificate is selected and click Next.

Certificate type

3. In the Domain names section, enter your main domain, for example, www.mydomain.com. In the Validation method section, make sure DNS validation is selected and click Request.

Domain names

Note: Did you see the Allow export section? It’s a relatively new feature in AWS that allows exporting public certificates issued by Amazon, including their private key, so you can install them wherever you want — for example, on an On-Premises server. This means that in the previous step, instead of using another public CA, we could have also used AWS Certificate Manager to obtain the certificate intended for our On-Premises server. 😮

4. The certificate has been created but is in a pending validation state. For ACM to verify that we truly own the domain specified in the certificate, we must create a CNAME record in the Authoritative DNS Provider.

  • In my case, I’m using Amazon Route 53 to manage my domain, so I can simply click the Create records in Route 53 button.

  • If you manage your domain in another Authoritative DNS Provider, then you must manually create the CNAME record using the CNAME name and CNAME value listed in the Domains section.

Create records

5. Finally, the AWS Certificate Manager certificate should move from the Pending validation state to the Issued state. This confirms that the certificate has been successfully issued in ACM.

Certificates Issued Status

Create the CloudFront Distribution

1. In the AWS console, navigate to the Amazon CloudFront service and click the Create a CloudFront distribution button.

Amazon CloudFront

2. In the Distribution options section, fill in the Distribution name field and, for this use case, make sure the Distribution type selected is Single website or app.

3. In the Custom domain section:

  • If the domain in question is being managed by Amazon Route 53 within the same AWS account where you are creating the CloudFront distribution, add the domain and click Check Domain.
  • If you manage that domain in another Authoritative DNS Provider or in Route 53 under a different AWS account, don’t worry — you’ll be able to add the custom domain in a later step.

Distribution options

4. Click Next.

5. In the Origin type section, since our origin is an On-Premises server, select Other.

6. In the Origin section, specify origin.mydomain.com as the Custom origin and leave the Origin path field empty.

Origin type

7. In the Settings section:

  • Under Origin settings, customize the parameters if you wish, or use the recommended configuration.
  • Under Cache settings, select Customize cache settings, and for Origin request policy, choose AllViewer. Customize other parameters if you wish.

Settings

8. Click Next.

9. In the Enable Security section, select Enable security protections. If it applies to your use case, enable SQL protection and Rate limiting. This will create an AWS WAF web ACL with basic protection and associate it with the CloudFront distribution.

Web Application Firewall

10. Click Next.

11. As mentioned in step 3, if the domain is managed by Route 53 in the same AWS account and you previously specified it, the Get TLS certificate section will automatically attempt to find a valid ACM certificate for that domain. In my case, it successfully found the certificate I created earlier.

  • If the domain is being managed by another Authoritative DNS Provider or in Route 53 under a different AWS account, don’t worry — you’ll be able to add the TLS certificate in a later step.

TLS Certificate

12. Click Next.

13. Review the distribution details and click Create distribution.

14. The CloudFront distribution will begin deploying. In the Last modified column, you’ll see that it’s in the Deploying state.

Deploying distribution

15. As mentioned in steps 3 and 11, if you manage your domain in another Authoritative DNS Provider or in Route 53 under a different AWS account, go to the Settings section and click Edit.

16. In the General section, specify your domain in Alternate domain name (CNAME) and select the ACM certificate you created earlier under Custom SSL certificate. Then click Save changes.

Edit settings

17. Once the distribution has finished deploying, under Last modified you’ll see the full timestamp of the latest modification. If the Alternate domain name and Custom SSL certificate are correctly configured, you’ve successfully completed the creation and setup of your CloudFront distribution.

Deployed distribution

18. Copy the Distribution domain name.

Configure DNS Routing

In my case, I’m managing my domain with Amazon Route 53, but you can create these DNS records in whichever Authoritative DNS Provider you’re using.

Create the CNAME record

1. If the domain in question is managed by Amazon Route 53 within the same AWS account where you’re creating the CloudFront distribution, take advantage and create a DNS record of type Alias, pointing your main domain www.mydomain.com to the CloudFront distribution.

Alias record

2. If the domain is managed in another Authoritative DNS Provider or in Route 53 from another AWS account, create a DNS record of type CNAME, pointing your main domain www.mydomain.com to the Distribution domain name of CloudFront.

CNAME record

Create the A record

3. Create a DNS record of type A pointing your new subdomain origin.mydomain.com to the public IP address of your On-Premises server.

A record

With this, you’ll have completed the necessary DNS configuration. However, we’re not done yet: if you try to access your server now via www.mydomain.com and origin.mydomain.com, both attempts will succeed, but there’s an important detail — origin.mydomain.com is not protected by WAF and therefore remains vulnerable. We’ll mitigate this risk in the next step.

How can you easily tell whether www.mydomain.com and origin.mydomain.com are protected by WAF or not? I use a Google Chrome extension called Wappalyzer, which allows you to identify the technologies used on any website. For example, when testing my domains, Wappalyzer detected that the www domain uses Amazon CloudFront as a CDN and Amazon Web Services as PaaS, while the origin domain uses neither of these services. This confirms that traffic to the origin domain does not pass through CloudFront and therefore is not inspected by AWS WAF. ⚠️

Wappalyzer comparison

Restrict Access to the On-Premises Server

Now we need to restrict access to the On-Premises server so that only traffic that passes through CloudFront — and has been inspected by AWS WAF — is accepted by the server.

To achieve this, we have two options:

  • Have CloudFront send an additional custom HTTP header with a secret value to the origin, and configure the origin to only accept traffic that contains this header with the correct secret value.
  • Configure the On-Premises server firewall to accept traffic only from CloudFront IP ranges specified in the ip-ranges.json file.

Since the second option would potentially require updating the On-Premises firewall rules every time Amazon modifies the ip-ranges.json file, I lean toward the first alternative, which is actually Amazon’s recommended option when you need to restrict access to an Application Load Balancer.

1. In the AWS console, select the CloudFront distribution. In the Origins tab, select the configured origin and click Edit.

Origins

2. In the Settings section, under Add custom header, click Add header and add a custom HTTP header. The value of this header must be secret and not publicly exposed. Save the changes.

Add custom header

Now CloudFront will add this HTTP header to all requests it sends to the origin.

3. Finally, configure the On-Premises web server to only accept requests that contain the custom HTTP header from CloudFront with the correct secret value.

With this, we’ve completed the implementation of AWS WAF for an On-Premises web application through CloudFront, restricting access through other means and ensuring that all traffic to the On-Premises server is securely inspected. 🔍


Cost Structure

The costs to consider in this implementation are as follows:

  • Amazon CloudFront (distribution, Price Class “Use all edge locations (best performance)”)
    • Data transfer out to the Internet (first tier): ~ USD 0.085 per GB for United States/Mexico/Canada/Europe.
    • HTTP/HTTPS request cost: for the USA it’s quoted at ~ USD 0.01 per 10,000 requests (~ USD 0.000001 per request) according to a recent guide.
    • Note: There’s a free tier of 1 TB of data transfer out + 10 million requests/month for CloudFront.
  • AWS WAF (Web ACL associated with CloudFront)
    • Web ACL cost: USD 5/month per Web ACL.
    • Cost per rule inside the Web ACL: USD 1/month per rule.
    • Cost per inspected request: USD 0.60 per million requests.
  • Amazon Route 53 (Hosted Zone + records)
    • Cost per public hosted zone: USD 0.50/month for the first 25 hosted zones.
    • Additional cost per record (if you have more than 10,000 per zone): USD 0.0015/month per additional record.
    • Standard DNS query cost: ~ USD 0.40 per million queries (first 1B queries).
  • AWS Certificate Manager (ACM Certificate for FQDN)
    • If it’s a public certificate managed by ACM, usage with integrated services (such as CloudFront) has no additional cost.
    • If it were an exportable or wildcard certificate, there would be fees, but in this case a single FQDN using CloudFront → estimated cost = USD 0/month.

It’s worth mentioning that the prices listed correspond to October 2025. 💲

Monthly Cost Estimate

Using my implementation as a reference with the following assumptions:

  • Region us-east-1
  • Outbound data (egress) from CloudFront: 1,000 GB/month
  • Requests to CloudFront: 10 million requests/month
  • One Web ACL in AWS WAF associated with the distribution
  • One hosted zone in Route 53 with one A record + one CNAME record
  • One ACM certificate for one FQDN
  • No advanced rules, no wildcard certificate, no massive DNS queries.

Calculations:

  • CloudFront — data transfer out
    • 1,000 GB × USD 0.085/GB = USD 85.00/month
  • CloudFront — requests
    • 10 million requests → (10,000,000 / 10,000) × USD 0.01 = 1,000 × USD 0.01 = USD 10.00/month
  • WAF — Web ACL
    • USD 5.00/month (one Web ACL)
  • WAF — rules
    • Assuming only 1 rule for simplicity → USD 1.00/month
  • WAF — inspected requests
    • 10 million requests × (USD 0.60 per million) = 10 × USD 0.60 = USD 6.00/month
  • Route 53 — hosted zone
    • USD 0.50/month (one public zone)
  • Route 53 — records
    • Only 2 records (A + CNAME) → none above threshold → USD 0.00 extra
  • Route 53 — DNS queries
    • Assuming modest use (for example, ~100,000 queries) → 0.1 million × USD 0.40 = USD 0.04 (approximately). For simplicity, rounded to ≈ USD 0.05/month
  • ACM — integrated public certificate
    • USD 0.00/month

Estimated Total:

Component Estimated Monthly Cost (USD)
CloudFront — data transfer out 85.00
CloudFront — requests 10.00
WAF — Web ACL 5.00
WAF — rules 1.00
WAF — inspected requests 6.00
Route 53 — hosted zone 0.50
Route 53 — records 0.00
Route 53 — DNS queries 0.05
ACM — certificate 0.00
Estimated Total ~ USD 107.55

Lessons Learned

In my experience, when creating certificates, specify the exact FQDN you will use. Do not use wildcards unless you truly need them, as wildcard certificates tend to be significantly more expensive and, in most cases, are not fully utilized. 💰

The WAF protection I implemented for CloudFront in this guide includes defense against common vulnerabilities such as those listed in the OWASP Top 10, SQL injections (SQL Injection), rate limiting (Rate Limiting), and blocking traffic from malicious IP addresses based on Amazon’s threat intelligence. However, if you deem it necessary—and in fact, it is recommended—customize your web ACL according to your requirements and add any additional rules you consider appropriate to protect your On-Premises server.

Please make sure to restrict access to the On-Premises server exclusively through CloudFront; otherwise, the server will remain exposed, without inspection or protection by WAF, and therefore vulnerable to attacks through other access paths. 🥲

I recommend renewing the secret value of the custom CloudFront HTTP header at least once a year as a preventive measure against potential security leaks. ⏱️

Conclusion

In conclusion, even if we have a web application hosted on an On-Premises server that we cannot migrate to the cloud, the managed service AWS WAF allows us to easily implement the firewall we need in the form of a web ACL with the appropriate rules, whether by using managed rules provided by Amazon or by creating custom rules tailored to our use case.

This way, we can inspect and filter all incoming traffic to our On-Premises server, ensuring that only traffic analyzed by AWS WAF is processed, thus strengthening the security posture of the application. 🥳

What’s Next

In the following section, you will find the official resources and documentation for the mentioned services, along with some points of interest related to the discussed topics, in case you want to continue learning or explore further to determine if they apply to your use case.

I also invite you to implement this solution in your own AWS account. Let me know in the comments what you thought of this guide or if you discovered something interesting during your implementation. ✍🏻

Official Resources

Top comments (0)