Back to articles
Technology Insight

Building a Custom CDN: Compiling Nginx from Source with Brotli 11 Compression on a Linux VPS

June 4, 2026

Introduction: The Case for a Private CDN with Advanced Compression

In the modern digital economy, web performance is directly tied to business outcomes. While commercial Content Delivery Networks (CDNs) offer convenient global caching, they often lack granular control over compression algorithms, caching policies, and data privacy. For organizations managing high-traffic web applications, media assets, or microservices, building a private, custom CDN provides a competitive edge.

By leveraging a high-performance Linux Virtual Private Server (VPS) and compiling Nginx from source, you can bypass the limitations of pre-packaged binaries. The crown jewel of this custom architecture is Brotli compression, specifically at its maximum compression ratio: Brotli Level 11 (Static). This guide provides a comprehensive, step-by-step blueprint for systems engineers and DevOps professionals looking to deploy their own CDN edge servers.

Why Brotli 11 and Why Custom Compilation?

Brotli, developed by Google, outperforms traditional Gzip by utilizing a modern variant of the LZ77 algorithm and Huffman coding, alongside a static dictionary. While Gzip maxes out its utility quickly, Brotli offers eleven levels of compression. However, running Brotli Level 11 dynamically on-the-fly is computationally expensive and can severely degrade CPU performance under heavy traffic loads.

The Solution: Static pre-compression. By compiling Nginx with the Google Brotli module, we can serve pre-compressed .br files at Level 11. This delivers the absolute smallest file sizes to the client without introducing any runtime CPU overhead.

Compiling Nginx from source is essential for this setup because standard repository packages (such as those from apt or yum) rarely include the upstream Brotli module by default. Source compilation also allows you to strip out unnecessary modules, optimizing the Nginx binary footprint for raw caching efficiency.

Prerequisites and Environment Setup

Before initiating the compilation process, ensure you have a clean Linux VPS. This guide assumes the use of Ubuntu 24.04 LTS or a similar Debian-based distribution, deployed with root or sudo privileges. Update your package repository and install the fundamental build tools required for compiling C code:

sudo apt update && sudo apt upgrade -y
sudo apt install -y build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev git libbrotli-dev libgeoip-dev

These dependencies handle core functionalities:

  • build-essential: Includes the GCC compiler and Make utilities.
  • libpcre3-dev: Manages regular expressions for Nginx location blocks.
  • libssl-dev: Enables robust TLS/SSL termination at the CDN edge.

Step-by-Step Compilation of Nginx with Brotli

1. Fetching the Source Code

We need to download both the Nginx source code and the official Google Brotli module. It is highly recommended to use the latest stable version of Nginx to ensure security patches are up to date.

cd /usr/local/src
sudo wget [https://nginx.org/download/nginx-1.26.1.tar.gz](https://nginx.org/download/nginx-1.26.1.tar.gz)
sudo tar -xzvf nginx-1.26.1.tar.gz

sudo git clone --recursive [https://github.com/google/ngx_brotli.git](https://github.com/google/ngx_brotli.git)

The --recursive flag is critical here, as it pulls the necessary underlying Brotli compression libraries embedded within the Git repository.

2. Configuring and Building the Binary

Navigate into the Nginx source directory and configure the compilation script. We will explicitly include the static and dynamic Brotli modules while tailoring Nginx for a lean CDN profile.

cd nginx-1.26.1

sudo ./configure --prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_v3_module \
--with-threads \
--add-module=/usr/local/src/ngx_brotli

sudo make
sudo make install

Once make install completes successfully, your custom Nginx binary with HTTP/2, HTTP/3, and Brotli support will be installed on your system path.

Configuring Nginx for Private CDN Functionality

With Nginx successfully compiled, we must configure it to operate as a high-efficiency caching reverse proxy. Open your central configuration file at /etc/nginx/nginx.conf and implement the structure outlined below.

Optimizing the Nginx Core

Ensure your worker processes are aligned with your VPS architecture to maximize throughput:

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

Implementing the Proxy Cache and Brotli Directive

Inside the http block, we define the caching zones and enable the static Brotli engine. This tells Nginx to look for a pre-compressed asset ending in .br whenever a client requests a file.

http {
    include       mime.types;
    default_type  application/octet-stream;

    # Logging Optimization
    log_format cdn_format '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          'Cache: $upstream_cache_status';
    access_log /var/log/nginx/access.log cdn_format;

    # Proxy Cache Paths
    proxy_cache_path /var/cache/nginx/cdn levels=1:2 keys_zone=cdn_cache:100m max_size=10g inactive=7d use_temp_path=off;

    # Brotli Configuration
    brotli on;
    brotli_static on;
    brotli_comp_level 6; # Dynamic compression fallback
    brotli_types text/plain text/css application/javascript application/json image/svg+xml;

    server {
        listen 443 ssl http2;
        server_name cdn.yourdomain.com;

        ssl_certificate /etc/letsencrypt/live/[cdn.yourdomain.com/fullchain.pem](https://cdn.yourdomain.com/fullchain.pem);
        ssl_certificate_key /etc/letsencrypt/live/[cdn.yourdomain.com/privkey.pem](https://cdn.yourdomain.com/privkey.pem);

        location / {
            proxy_pass http://your_origin_server.com;
            proxy_cache cdn_cache;
            proxy_cache_valid 200 302 24h;
            proxy_cache_valid 404 1m;
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
            proxy_cache_lock on;
            
            # Forward headers to origin
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # Add Cache Status to response headers for testing
            add_header X-Cache-Status $upstream_cache_status;
            add_header Vary Accept-Encoding;
        }
    }
}

Automating Static Brotli 11 Pre-Compression

Since dynamically compressing assets at Level 11 in real-time will cause CPU spikes that delay the Time to First Byte (TTFB), we must generate these static files beforehand. We can use a simple shell script integrated into your deployment pipeline or cron jobs to scan your static assets directory and compress them using the standalone Brotli CLI tool.

Install the Brotli CLI tool on your build server or origin:

sudo apt install brotli -y

Use this script to find all JavaScript, CSS, and HTML assets and generate .br files at maximum compression:

#!/bin/bash
TARGET_DIR="/var/www/cdn_assets"

find "$TARGET_DIR" -type f \( -name "*.js" -o -name "*.css" -o -name "*.html" -o -name "*.svg" \) | while read -r file; do
    # Skip if a newer .br file already exists
    if [ ! -f "$file.br" ] || [ "$file" -nt "$file.br" ]; then
        echo "Compressing: $file at Level 11"
        brotli -f -11 "$file"
    fi
done

When Nginx serves a file, the brotli_static on; directive checks if the client sends an Accept-Encoding: br header. If it does, Nginx transparently delivers the ultra-small .br file generated by this script directly from the disk, bypassing runtime compression entirely.

Verification, Testing, and Performance Monitoring

After starting your Nginx service (sudo systemctl start nginx), verify that your custom CDN edge server is delivering compressed files correctly. You can test this using curl from any terminal terminal:

curl -I -H "Accept-Encoding: br" [https://cdn.yourdomain.com/assets/app.js](https://cdn.yourdomain.com/assets/app.js)

Analyze the HTTP response headers. You should look for two critical indicators:

  1. Content-Encoding: br — Verifies that the Brotli compression engine is working.
  2. X-Cache-Status: HIT — Indicates that the custom Nginx server successfully cached the asset from your origin and is serving it directly from the VPS SSD.

Summary Table of Expected Improvements

Compression AlgorithmAverage File Size ReductionCPU Overhead (Edge Server)Ideal Use Case
Gzip Level 6~65%LowLegacy clients, dynamic content
Dynamic Brotli Level 6~72%ModerateDynamic JSON APIs, personalized pages
Static Brotli Level 11~81%Zero (Pre-compiled)Production JS, CSS, fonts, SVG graphics

Conclusion

By compiling Nginx from source on a Linux VPS and implementing static Brotli level 11 compression, you effectively build a highly streamlined, specialized CDN edge server. This architecture eliminates unnecessary commercial overhead, delivers best-in-class data compression ratios, and keeps total data ownership inside your private infrastructure. As your traffic scales, you can easily replicate this configuration across multiple geographic nodes, utilizing Anycast DNS routing to transform your single VPS setup into a globally distributed enterprise CDN network.

Building a Custom CDN: Compiling Nginx from Source with Brotli 11 Compression on a Linux VPS | DPTCloud