Install and Configure Apache Traffic Server as a Caching Reverse Proxy on Ubuntu

Written by: Bagus Facsi Aginsa
Published at: 15 May 2026


If you have ever run a high-traffic web service, you know the pain: your origin server gets hammered with requests for the same content over and over, images, CSS files, API responses that barely change, HTML pages that look identical to thousands of users. Every one of those requests burns CPU, database queries, and bandwidth on your backend.

A caching reverse proxy solves this by sitting between your users and your origin server. It stores a copy of the response the first time it is fetched, and serves that copy directly for subsequent requests, without touching the origin at all. Your backend load drops, your response times improve, and your infrastructure scales further without spending more money.

Apache Traffic Server (ATS) is one of the most capable open-source caching reverse proxies available. It was originally developed at Yahoo, donated to the Apache Software Foundation, and is now used in production by companies like LinkedIn, Comcast, and Apple’s CDN. It is battle-tested for extremely high request volumes.

In this tutorial, you will install Apache Traffic Server on Ubuntu, configure it as a caching reverse proxy in front of a backend web server, set up URL remapping rules, tune the cache, and learn how to verify that caching is actually working.


What Is Apache Traffic Server?

Apache Traffic Server operates as an HTTP proxy. You point DNS or a load balancer at it, and it forwards requests to one or more origin servers according to rules you define. On the way back, it stores cacheable responses in a local cache, in RAM, on disk, or both.

Unlike Nginx or HAProxy, ATS was designed specifically around caching and proxying at scale. Its cache engine handles millions of objects efficiently and respects HTTP cache control headers out of the box.

There are two modes to know about:

  • Forward proxy: clients are explicitly configured to send requests through ATS. Used for egress caching.
  • Reverse proxy: ATS sits in front of your servers and is transparent to clients. This is what you will set up here.

In reverse proxy mode, the client sees ATS as if it were the origin server. ATS uses its remap rules to map incoming hostnames and paths to the correct backend URL.

How the Cache Works

When a request arrives, ATS checks whether it has a fresh cached copy. If it does (cache hit), it serves the response immediately without contacting the backend. If it does not (cache miss), it fetches the response from the origin, stores it, and returns it to the client.

What gets cached and for how long is controlled by:

  • HTTP response headers from the origin (Cache-Control, Expires, Last-Modified)
  • ATS configuration overrides when you need to cache content that the origin does not explicitly mark as cacheable

Prerequisites

Before you begin, make sure you have:

  • A server running Ubuntu 22.04 LTS or later
  • A user account with sudo privileges
  • A backend web server or application already running (we will use a simple Nginx instance on the same machine for the examples port 8080)
  • A domain name or hostname pointed at your server (or just use the server’s IP for testing)
  • Basic comfort with editing files and running commands

If you want to follow along with a real backend, you can quickly spin up Nginx listening on port 8080 with sudo apt install nginx and a minimal server block. But any HTTP service reachable from the ATS server works.


Step 1: Install Apache Traffic Server

The trafficserver package is available in Ubuntu’s default repositories.

sudo apt update
sudo apt install trafficserver -y

After installation, start and enable the service:

sudo systemctl start trafficserver
sudo systemctl enable trafficserver

Verify it is running:

sudo systemctl status trafficserver

You should see active (running). ATS logs to /var/log/trafficserver/ and its configuration lives in /etc/trafficserver/.


Step 2: Understand the Configuration Directory

All ATS configuration is in /etc/trafficserver/. The files you will work with most are:

File Purpose
records.config Global settings such as ports, cache behavior, timeouts
remap.config URL remapping rules, maps incoming URLs to backend URLs
cache.config Fine-grained cache rules per URL pattern
storage.config Defines where and how much disk space the cache uses
ip_allow.yaml Access control to config which IPs can connect

Every time you change a config file, you can reload without restarting the process:

sudo traffic_ctl config reload

Step 3: Configure the Listening Port and Reverse Proxy Mode

Open the main configuration file:

sudo nano /etc/trafficserver/records.config

Find and update these settings. If a line already exists, change its value. If it does not, add it:

CONFIG proxy.config.http.server_ports STRING 80
CONFIG proxy.config.reverse_proxy.enabled INT 1
CONFIG proxy.config.url_remap.remap_required INT 1
CONFIG proxy.config.http.cache.http INT 1

What each setting does:

  • proxy.config.http.server_ports STRING 80 tells ATS to listen on port 80. If you also want port 443 (HTTPS termination), you would add ssl to this string.
  • proxy.config.reverse_proxy.enabled INT 1 enables reverse proxy mode. Without this, ATS behaves as a forward proxy.
  • proxy.config.url_remap.remap_required INT 1 makes ATS reject any request that does not match a remap rule. This is a safety setting; it prevents ATS from accidentally proxying traffic to arbitrary destinations.
  • proxy.config.http.cache.http INT 1 enables HTTP caching. This should already be on by default, but it is worth confirming.

Save and close the file.


Step 4: Set Up URL Remapping

The remap.config file is where you tell ATS which backend to send traffic to for each hostname or path. Every request must match a rule here (because you set remap_required to 1).

Open the remap file:

sudo nano /etc/trafficserver/remap.config

The basic syntax is:

map <incoming-url> <backend-url>

For example, if your domain is example.com and your backend runs on the same machine on port 8080:

map http://example.com/ http://127.0.0.1:8080/

If you have multiple services, add multiple rules:

map http://example.com/        http://127.0.0.1:8080/
map http://api.example.com/    http://127.0.0.1:3000/
map http://static.example.com/ http://127.0.0.1:8081/

ATS matches requests by the full URL including path prefix. If you want to rewrite paths as well, you can use the @plugin=conf_remap.so or the built-in @action=redirect syntax, but for a basic setup, the above is enough.

After saving the file, reload the configuration:

sudo traffic_ctl config reload

Test that ATS is now proxying traffic to your backend:

curl -I http://localhost/ -H "Host: example.com"

You should see a response from your backend, plus an extra header that ATS adds:

Via: http/1.1 your-hostname (ApacheTrafficServer/9.x.x [...]
Age: 0

The Via header confirms ATS handled the request. Age: 0 means this was a cache miss (the response was freshly fetched from the origin).


Step 5: Configure Cache Storage

By default, ATS uses a RAM cache and a small disk cache in /var/cache/trafficserver/. You can tune both.

Open the storage configuration:

sudo nano /etc/trafficserver/storage.config

The default entry looks like:

/var/cache/trafficserver 256M

This tells ATS to use 256 MB of disk space in that directory for the object cache. For a real deployment, you would point this at a dedicated disk or partition and increase the size. For example, using a full disk device for maximum performance:

/dev/sdb 50G

For the RAM cache, set the size in records.config:

CONFIG proxy.config.cache.ram_cache.size INT 512M

This allocates 512 MB of RAM for the most frequently accessed objects, dramatically reducing disk I/O for hot content.

After any storage changes, you need to restart ATS (not just reload), because the cache engine initializes storage at startup:

sudo systemctl restart trafficserver

Step 6: Control What Gets Cached

ATS respects Cache-Control headers from the origin by default. If your backend sends Cache-Control: no-store or Cache-Control: private, ATS will not cache those responses.

If your backend does not send proper cache headers or you want to override them, you can use cache.config.

Open it:

sudo nano /etc/trafficserver/cache.config

To cache all responses from a specific backend for 10 minutes regardless of headers:

dest_domain=example.com  ttl-in-cache=600

To never cache responses to authenticated requests (a good default for security):

dest_domain=example.com  scheme=http  header=Authorization  action=never-cache

To cache only GET requests (ATS does this by default, but it is good to know):

dest_domain=example.com  method=get

Reload after changes:

sudo traffic_ctl config reload

Step 7: Verify Caching Is Working

This is the step most tutorials skip, but it is the most important one, you need to confirm that the cache is actually storing and serving objects.

Method 1: Check the Age header

Make the same request twice:

curl -I http://localhost/ -H "Host: example.com"
curl -I http://localhost/ -H "Host: example.com"

On the second request, watch for the Age header. A value greater than zero means the response was served from cache:

Age: 3

Age: 0 always means ATS fetched it fresh from the origin (cache miss or uncacheable).

Method 2: Check the X-Cache header

ATS can add a diagnostic header. Enable it by adding this to records.config:

CONFIG proxy.config.http.insert_age_in_response INT 1

Some ATS versions and plugins also add an X-Cache header — but Age is the reliable indicator.

Method 3: Check cache metrics

ATS exposes runtime metrics through traffic_ctl:

sudo traffic_ctl metric get proxy.process.http.cache_hit_fresh
sudo traffic_ctl metric get proxy.process.http.cache_miss_cold

Watch these increment over time. A healthy deployment should see far more hits than misses after warming up.


Common Mistakes and Troubleshooting

ATS is not caching responses

Check what headers your origin is sending:

curl -I http://127.0.0.1:8080/

If you see Cache-Control: no-store, Cache-Control: private, or Pragma: no-cache, ATS will honor those and skip caching. Either fix the origin headers or use cache.config to override with ttl-in-cache.

Getting “Connection refused” on port 80

Confirm ATS is actually listening:

sudo ss -tlnp | grep 80

If ATS is not listed, check records.config for proxy.config.http.server_ports and confirm the service is running with systemctl status trafficserver.

Remap not matching — getting 404 or “Host header required”

Make sure your Host header in the request matches the URL in remap.config exactly, including the scheme. Test with:

curl -v http://127.0.0.1/ -H "Host: example.com"

If you see an ATS error page mentioning “no remap entry,” your rule is not matching. Check for typos in remap.config.

Config changes are not taking effect

Not all settings are hot-reloadable. After changing records.config entries that affect the cache engine or listening ports, do a full restart:

sudo systemctl restart trafficserver

Checking the ATS error log

When something is wrong and you cannot figure out why:

sudo tail -f /var/log/trafficserver/error.log

This gives you ATS’s internal view of what is happening on each request.


Best Practices

Set conservative cache TTLs at first. It is better to cache for 5 minutes and confirm correctness than to cache for a day and serve stale data. Gradually increase TTLs once you trust the behavior.

Do not cache authenticated or personalized responses. Always add a cache.config rule that prevents caching when an Authorization or Cookie header is present for endpoints that return user-specific data.

Use a dedicated disk for the cache. A separate disk (or even better, an NVMe SSD) for cache storage dramatically improves throughput. Avoid storing the cache on the same disk as your OS or application logs.

Monitor your hit rate. A cache hit rate below 40–50% usually means either your TTLs are too short, your content has high variance (lots of unique URLs), or you are caching too little RAM/disk. Use traffic_ctl metric to watch hit and miss counts over time.

Keep ATS up to date. Security fixes and performance improvements appear regularly. Track upstream releases at the Apache Traffic Server releases page.

Pair ATS with a proper firewall. Port 80 should only be reachable from trusted sources or the public internet as intended. Lock down the ATS admin port (default 8083) so only localhost or your monitoring system can reach it:

sudo ufw allow 80/tcp
sudo ufw deny 8083
sudo ufw enable

Conclusion

You have installed Apache Traffic Server on Ubuntu, configured it as a caching reverse proxy, mapped incoming requests to a backend server, tuned the cache storage, and learned how to verify that caching is working in practice.

ATS is genuinely powerful for high-traffic scenarios. A properly configured ATS instance can absorb the majority of your request volume and reduce origin load by 70–90% for cacheable content — which means your backend servers can handle far more real load with the same hardware.

From here, good next steps include:

  • Adding SSL termination: configure ATS to accept HTTPS connections and forward HTTP to the backend, similar to what Certbot does with Nginx and Let’s Encrypt
  • Exploring the header_rewrite plugin: lets you modify request and response headers without writing custom code
  • Setting up origin health checks: ATS supports parent selection with health checking so it can route around a failed backend
  • Reading the ATS documentation: the official docs at docs.trafficserver.apache.org are dense but comprehensive, especially the records.config reference

Once you are comfortable with the basics, ATS can grow with you — from a single reverse proxy node to a multi-tier CDN-style deployment with parent and child caches.