Back to blog
Guide March 27, 2026

How to Fix "could not build the server_names_hash" in Nginx

PPingzo Infrastructure Team
Automate WhatsApp Alerts
Start Free ➔

When configuring hundreds of virtual hosts, long subdomains, or wildcard routing in Nginx, running nginx -t or reloading the service may abruptly fail with one of the following error messages:

nginx: [emerg] could not build the server_names_hash,
you should increase server_names_hash_bucket_size: 32

or:

nginx: [emerg] could not build the server_names_hash,
you should increase either server_names_hash_max_size: 512
or server_names_hash_bucket_size: 64

This error prevents Nginx from reloading or booting, dropping incoming traffic if a system restart is attempted. In this guide, we break down the internal mechanics of Nginx's static hash table allocation, explain how CPU L1 cache-line sizing dictates bucket limits, and provide the exact directives needed to resolve the error across Debian, Ubuntu, RHEL, and Docker environments.


[!TIP] Proactive Infrastructure Verification If your web servers fail to reload or silently drop virtual host routing due to hash collisions, test your endpoints and DNS mappings immediately using our free DNS Lookup Tool, HTTP Header Checker, and Ping Test Tool.


The Internal Mechanics of Nginx Hash Tables

Nginx uses static hash tables (ngx_hash_t) to achieve near-$\mathcal{O}(1)$ lookup performance for hostnames, MIME types, variables, and request headers. Instead of evaluating server names sequentially with regular expressions on every incoming request, Nginx builds an optimized in-memory lookup table during startup.

How Nginx Allocates Server Names

When Nginx parses the configuration files during initialization, it executes ngx_hash_init() and organizes domain names into three separate hash tables:

  1. Exact Hostnames: Full domains such as api.example.com or app.internal.domain.
  2. Leading Wildcard Hostnames: Domains starting with a wildcard, such as *.example.com (stored in reverse order, e.g., com.example.*).
  3. Trailing Wildcard Hostnames: Domains ending with a wildcard, such as mail.*.
  4. Regular Expressions: Evaluated sequentially only if no exact or wildcard match is found.

Each bucket in the hash table stores the hash key, the pointer to the name string, and the pointer to the corresponding virtual host configuration structure (ngx_http_core_srv_conf_t).

+-------------------------------------------------------------------+
|                     Nginx Memory Hash Table                       |
+-------------------------------------------------------------------+
| Bucket 0: [ Key | Name Ptr | Conf Ptr ] -> [ Key | Ptr | Conf ]   |
| Bucket 1: [ Key | Name Ptr | Conf Ptr ]                           |
| Bucket 2: [ Empty ]                                               |
| Bucket 3: [ Key | Name Ptr | Conf Ptr ] -> [ Key | Ptr | Conf ]   |
| ...                                                               |
| Bucket N: [ Key | Name Ptr | Conf Ptr ]                           |
+-------------------------------------------------------------------+
   ^                                               ^
   |-- server_names_hash_max_size (Table Limit)    |-- server_names_hash_bucket_size (Bucket Capacity)

Why Hash Construction Fails

The hash generation algorithm calculates the total number of buckets required based on the total number of names and their length. Hash construction fails when:

  1. Bucket Overflow: The longest domain name (including null terminator and structural overhead) exceeds the memory capacity of a single bucket (server_names_hash_bucket_size).
  2. Table Saturation: The hash table experiences too many hash collisions, and the total number of buckets exceeds server_names_hash_max_size.

Mathematically, if $L_{\max}$ represents the byte length of the longest configured domain string, $S_{\text{header}}$ represents the 8-byte Nginx hash element header, and $B$ represents server_names_hash_bucket_size, the minimum condition for a single key to fit within a bucket is:

$$\text{Bucket Capacity} \ge L_{\max} + S_{\text{header}} + \text{sizeof}(\text{void}*)$$

If this inequality is violated, Nginx terminates initialization immediately with an emergency log.


Step 1: Diagnose the Exact Failure Vector

Before modifying directives, identify whether your failure is driven by an excessively long domain name or an aggregate table saturation.

1. Run Syntax Verification

Execute the configuration test to read the diagnostic output:

sudo nginx -t

Example failure output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: [emerg] could not build the server_names_hash, you should increase server_names_hash_bucket_size: 64
nginx: configuration file /etc/nginx/nginx.conf test failed

2. Identify the Longest Domain Names

Audit all configured server_name entries across /etc/nginx/ to inspect long hostnames or unintended concatenation syntax errors:

# Extract all server_name declarations and sort by length
grep -rohP 'server_name\s+[^;]+;' /etc/nginx/ | \
  sed -e 's/server_name//g' -e 's/;//g' | \
  tr -s ' ' '\n' | \
  awk '{ print length, $0 }' | \
  sort -n -r | \
  head -n 20

Common configuration mistakes uncovered by this command include:

  • A missing semicolon ; between server blocks, causing Nginx to treat multiple domains as a single 150-character string.
  • Automatically generated AWS ELB, Kubernetes ingress, or CloudFront CNAME aliases that exceed standard 32-byte or 64-byte boundaries.

3. Check CPU L1 Cache Line Size

For optimal memory throughput, server_names_hash_bucket_size should always be an integer power of two ($32, 64, 128$) and aligned with the host CPU's L1 data cache line size:

# Check L1 Data Cache Line Size on Linux
getconf LEVEL1_DCACHE_LINESIZE

On modern x86_64 and ARM64 servers, this command typically outputs 64. Setting the bucket size to equal or double this value prevents cache-line splitting during memory access.


Step 2: Configure Hash Table Directives

Nginx hash directives must be declared inside the top-level http {} context in /etc/nginx/nginx.conf. Defining them inside a server {} or location {} block will trigger a directive placement error.

1. Update nginx.conf

Open /etc/nginx/nginx.conf in your preferred editor:

sudo nano /etc/nginx/nginx.conf

Locate the http block and define or increase server_names_hash_bucket_size and server_names_hash_max_size:

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

    # -----------------------------------------------------------
    # Server Names Hash Optimization
    # -----------------------------------------------------------
    # Increase bucket size to accommodate long FQDNs (powers of 2: 64, 128)
    server_names_hash_bucket_size 64;

    # Increase maximum table size if managing hundreds of vhosts (512, 1024, 2048)
    server_names_hash_max_size 1024;

    # Connection and I/O settings
    sendfile        on;
    keepalive_timeout  65;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

2. Sizing Recommendations by Fleet Scale

Fleet / Domain ScaleTypical Host CountRecommended bucket_sizeRecommended max_size
Standard Server1 – 20 vhosts64512 (default)
SaaS Multi-Tenant20 – 200 custom domains64 or 1281024
Large API Gateway / CDN Edge200 – 1000+ vhosts1282048 or 4096
Automated Cloud StagingLong AWS/GCP dynamic subdomains1281024

Step 3: Test and Apply Configuration

After updating the directives:

1. Validate Configuration Syntax

sudo nginx -t

Expected successful output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If Nginx still reports an error requesting a larger bucket size (e.g., you should increase server_names_hash_bucket_size: 128), double the value from 64 to 128 in nginx.conf and re-test.

2. Reload Nginx Without Dropping Connections

Apply the changes gracefully without interrupting active TLS sessions:

# Systemd systems (Ubuntu, Debian, RHEL, Rocky)
sudo systemctl reload nginx

# Direct signal reload
sudo nginx -s reload

Edge Case: Docker & Automated Reverse Proxies

In containerized environments (such as Docker Compose, Kubernetes Ingress, or Traefik/Nginx sidecars), dynamically generated configuration templates often omit hash tuning directives.

Docker Compose Example

Mount a custom nginx.conf snippet or supply the directive in your base image:

services:
  reverse-proxy:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./conf.d:/etc/nginx/conf.d:ro

Ensure ./nginx.conf sets server_names_hash_bucket_size 128; inside the http {} block before loading ./conf.d/*.conf.


Hash Table Configuration Directives Comparison

DirectiveContextDefault ValuePurpose
server_names_hash_bucket_sizehttp32 / 64 (Arch/OS dependent)Maximum memory byte size allocated per hash bucket. Must increase when individual domain names are long.
server_names_hash_max_sizehttp512Total number of buckets in the hash table. Must increase when total number of virtual hosts causes hash collisions.
types_hash_bucket_sizehttp64Hash bucket size for MIME type mappings (mime.types).
variables_hash_max_sizehttp1024Maximum table size for Nginx runtime variables.

Enterprise Monitoring & Infrastructure Resilience with Pingzo

Preventing silent configuration crashes and virtual host routing failures requires continuous external synthetic monitoring. When Nginx fails to reload or drops specific virtual hosts due to misconfigured routing, Pingzo detects the outage within seconds.

+-----------------------------------------------------------------------------+
|                         Pingzo Global Edge Network                          |
+-----------------------------------------------------------------------------+
               |                                   |
    [ Multi-Region HTTP/S Checks ]        [ Multi-Domain SSL & DNS Checks ]
               |                                   |
               v                                   v
+-----------------------------------------------------------------------------+
|               Your Nginx Reverse Proxy / Load Balancer Cluster              |
|        (Alerting on 502, 500, Hash Mismatch, or Dead Virtual Hosts)         |
+-----------------------------------------------------------------------------+
               |
               v
+-----------------------------------------------------------------------------+
|                 Instant Alerts via WhatsApp, Slack, & Telegram               |
+-----------------------------------------------------------------------------+

Pingzo Monitoring Plans

FeatureFreeStarter ($5/mo)Pro ($12/mo)Agency ($29/mo)
Check Frequency15 minutes5 minutes2 minutes1 minute
Monitors1 monitor10 monitorsUnlimitedUnlimited
Multi-Location TestingGlobalGlobalGlobalGlobal
SSL & DNS AuditsBasicAdvancedReal-TimeReal-Time + Custom Root
Alert ChannelsEmailEmail, Telegram, WhatsApp, Slack, DiscordAll Channels + Multi-RecipientWebhooks, SMS, White-Label Dashboards
Multi-Tenant ManagementNoNoNoYes (Multi-Team)

Deploy full-stack synthetic uptime monitoring and multi-host validation today at Pingzo.

Zero-Code Uptime Alerts

Stop Finding Out About Outages from Angry Users

Get instant WhatsApp & Discord alerts the second your API, website, or server goes down. Setup in 30 seconds with 60-second checks.

WhatsApp & Discord 60-Second Checks Free Forever Plan
Try Pingzo Free

Know before your users do

Connect official WhatsApp notification channels, Discord webhooks, Telegram bots, and public status pages. Start in 30 seconds.

Create Free Monitor