Infrastructure & Networking

What is Nginx?

Nginx, pronounced engine-x, is an open-source web server, reverse proxy, load balancer, and HTTP cache that has become one of the most widely deployed pieces of web infrastructure in the world. Created by Igor Sysoev and first publicly released in 2004 Nginx was designed specifically to address the C10K problem, the challenge of handling ten thousand simultaneous connections on a single server, a problem that Apache’s process-based architecture struggled to solve efficiently.

Nginx powers a significant proportion of the world’s highest-traffic websites, its combination of high performance, low memory footprint, and flexible configuration has made it the dominant choice for production web servers, reverse proxies, and load balancers in modern web infrastructure. Nginx is deployed as a standalone web server serving static content, as a reverse proxy in front of application servers, Node.js, Python, Ruby, PHP, as a load balancer distributing traffic across server pools, and as an HTTP cache reducing origin server load.

The architectural difference between Nginx and its primary predecessor Apache is fundamental. Apache uses a process-per-connection or thread-per-connection model, each connection requires a dedicated process or thread consuming significant memory. Under high concurrency Apache’s memory consumption grows proportionally with the number of connections. Nginx uses an event-driven, asynchronous, non-blocking architecture, a small fixed number of worker processes each handle thousands of connections simultaneously using event loops. Nginx’s memory consumption remains low and predictable regardless of connection count.

For redirect management Nginx is one of the most common implementation environments, its return and rewrite directives provide straightforward, performant redirect configuration. Understanding Nginx redirect syntax is essential for managing redirects on Nginx-based infrastructure.

Nginx architecture

Nginx’s event-driven architecture is the foundation of its performance characteristics, understanding how it works explains why it handles high concurrency more efficiently than process-based web servers.

Master and worker processes: Nginx runs as a master process that manages worker processes. The master process reads configuration, binds to network ports, a privileged operation requiring root, and spawns worker processes. Worker processes handle actual request processing, accepting connections, reading requests, executing configuration logic, and writing responses. The number of worker processes is typically set to match the number of CPU cores, worker_processes auto: enabling full CPU utilisation.

The master process handles configuration reloading and binary upgrades without dropping connections, sending signals to worker processes to gracefully finish in-flight requests before accepting new configuration. This zero-downtime configuration reloading is operationally valuable, redirect rule updates can be applied without interrupting active connections.

Event-driven request handling: each Nginx worker process uses an event loop, a mechanism that monitors many connections simultaneously and processes events, data arrival, connection establishment, connection closure, as they occur. A worker process handles a connection event, reading request data, and moves on to handle other events while waiting for the next data from that connection. The worker never blocks waiting for I/O, it processes hundreds or thousands of connections concurrently within a single process.

This event-driven model achieves high concurrency with minimal resource consumption. A single Nginx worker process handles thousands of simultaneous connections, the total memory footprint for handling 10,000 connections might be 10-30 MB, compared to Apache’s process-per-connection model that might require 100-300 MB for the same connection count.

Configuration structure: Nginx configuration is defined in a hierarchical block structure. The main configuration file, typically /etc/nginx/nginx.conf: contains global settings and includes additional configuration files. http blocks contain web server configuration. server blocks within http define virtual hosts, configuration for specific domains. location blocks within server define configuration for specific URL paths.

http {
    server {
        listen 443 ssl;
        server_name example.com;
        
        location / {
            # Configuration for all paths
        }
        
        location /api/ {
            # Configuration for API paths

http {
    server {
        listen 443 ssl;
        server_name example.com;
        
        location / {
            # Configuration for all paths
        }
        
        location /api/ {
            # Configuration for API paths

http {
    server {
        listen 443 ssl;
        server_name example.com;
        
        location / {
            # Configuration for all paths
        }
        
        location /api/ {
            # Configuration for API paths

Nginx redirect configuration

Nginx provides several mechanisms for implementing redirects: each appropriate for different scenarios.

The return directive: the preferred mechanism for simple redirects. The return directive immediately returns a response with the specified status code and optional URL, short-circuiting all further request processing. No regex matching, no rewriting, just an immediate redirect response. return is the most efficient redirect mechanism in Nginx.

# Simple 301 redirect to a new URL
location /old-page {
    return 301 https://example.com/new-page;
}

# Redirect preserving the full request URI
server {
    server_name old-domain.com;
    return 301 https://new-domain.com$request_uri;
}

# Redirect with a specific status code
location /moved {
    return 302 https://example.com/temporary-destination

# Simple 301 redirect to a new URL
location /old-page {
    return 301 https://example.com/new-page;
}

# Redirect preserving the full request URI
server {
    server_name old-domain.com;
    return 301 https://new-domain.com$request_uri;
}

# Redirect with a specific status code
location /moved {
    return 302 https://example.com/temporary-destination

# Simple 301 redirect to a new URL
location /old-page {
    return 301 https://example.com/new-page;
}

# Redirect preserving the full request URI
server {
    server_name old-domain.com;
    return 301 https://new-domain.com$request_uri;
}

# Redirect with a specific status code
location /moved {
    return 302 https://example.com/temporary-destination

$request_uri: a built-in Nginx variable, contains the full URI of the request including path and query string. Using $request_uri in redirect destinations preserves the original path and query string, ensuring redirected users arrive at the equivalent page on the destination rather than the homepage.

The rewrite directive: more powerful than return: supports regular expression pattern matching and capture groups for complex URL transformations.

# Redirect with regex pattern matching
rewrite ^/blog/(\d{4})/(\d{2})/(.+)$ /blog/$3 permanent;

# Redirect file extension change
rewrite ^(.+)\.html$ $1 permanent

# Redirect with regex pattern matching
rewrite ^/blog/(\d{4})/(\d{2})/(.+)$ /blog/$3 permanent;

# Redirect file extension change
rewrite ^(.+)\.html$ $1 permanent

# Redirect with regex pattern matching
rewrite ^/blog/(\d{4})/(\d{2})/(.+)$ /blog/$3 permanent;

# Redirect file extension change
rewrite ^(.+)\.html$ $1 permanent

The permanent flag produces a 301 redirect. The redirect flag produces a 302. Capture groups, (\d{4}), (\d{2}), (.+): capture portions of the matched URL for use in the destination, $3 references the third capture group.

While rewrite is more flexible than return it is also more complex and potentially slower, Nginx must evaluate the regex for each request. For simple redirects return is preferred. For pattern-based URL transformations rewrite is appropriate.

HTTP to HTTPS redirect: the universal redirect pattern in Nginx configuration:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;
    
    # SSL configuration
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;
    
    # Site content
    location / {
        # serve content

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;
    
    # SSL configuration
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;
    
    # Site content
    location / {
        # serve content

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;
    
    # SSL configuration
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;
    
    # Site content
    location / {
        # serve content

The HTTP server block on port 80 returns a 301 redirect to HTTPS for all requests. The HTTPS server block on port 443 handles the actual content serving after the redirect.

www to non-www redirect:

server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 https

server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 https

server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 https

Domain migration redirect: redirecting all traffic from an old domain to a new domain:

server {
    listen 80;
    listen 443 ssl;
    server_name old-domain.com www.old-domain.com;
    
    ssl_certificate /path/to/old-domain.crt;
    ssl_certificate_key /path/to/old-domain.key;
    
    return 301 https://new-domain

server {
    listen 80;
    listen 443 ssl;
    server_name old-domain.com www.old-domain.com;
    
    ssl_certificate /path/to/old-domain.crt;
    ssl_certificate_key /path/to/old-domain.key;
    
    return 301 https://new-domain

server {
    listen 80;
    listen 443 ssl;
    server_name old-domain.com www.old-domain.com;
    
    ssl_certificate /path/to/old-domain.crt;
    ssl_certificate_key /path/to/old-domain.key;
    
    return 301 https://new-domain

Note that the old domain requires a valid SSL certificate for HTTPS requests to be received and redirected, requests arriving over HTTPS must complete the TLS handshake before the redirect response can be sent.

Nginx as a reverse proxy

Nginx’s reverse proxy capabilities are as widely used as its web server capabilities, particularly for proxying requests to application servers.

Basic reverse proxy configuration: forwarding requests to an application server:

server {
    listen 443 ssl;
    server_name example.com;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto

server {
    listen 443 ssl;
    server_name example.com;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto

server {
    listen 443 ssl;
    server_name example.com;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto

proxy_pass specifies the backend server address. proxy_set_header directives forward original request information, the host, client IP, and protocol, to the backend server. Without these headers the backend server sees all requests originating from localhost rather than from real clients.

Upstream load balancing: distributing requests across multiple backend servers:

upstream app_servers {
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;
    
    # Optional: least connections algorithm
    least_conn;
    
    # Optional: sticky sessions by IP hash
    # ip_hash;
}

server {
    location / {
        proxy_pass http

upstream app_servers {
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;
    
    # Optional: least connections algorithm
    least_conn;
    
    # Optional: sticky sessions by IP hash
    # ip_hash;
}

server {
    location / {
        proxy_pass http

upstream app_servers {
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;
    
    # Optional: least connections algorithm
    least_conn;
    
    # Optional: sticky sessions by IP hash
    # ip_hash;
}

server {
    location / {
        proxy_pass http

The upstream block defines a named server pool. proxy_pass references the pool name. Nginx distributes requests across the pool according to the configured algorithm, round-robin by default.

Nginx SSL configuration

Nginx handles SSL termination: accepting HTTPS connections from browsers and forwarding decrypted HTTP to backend servers.

SSL certificate configuration:

server {
    listen 443 ssl;
    server_name example.com;
    
    # Certificate and private key
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # Modern TLS configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    
    # HSTS header
    add_header Strict-Transport-Security "max-age=31536000"

server {
    listen 443 ssl;
    server_name example.com;
    
    # Certificate and private key
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # Modern TLS configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    
    # HSTS header
    add_header Strict-Transport-Security "max-age=31536000"

server {
    listen 443 ssl;
    server_name example.com;
    
    # Certificate and private key
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # Modern TLS configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    
    # HSTS header
    add_header Strict-Transport-Security "max-age=31536000"

Let’s Encrypt with Certbot: the most common SSL certificate management approach for Nginx. Certbot automatically provisions and renews Let’s Encrypt certificates and modifies Nginx configuration to use them:

certbot --nginx -d example.com -d
certbot --nginx -d example.com -d
certbot --nginx -d example.com -d

Certbot installs the certificate, configures the Nginx SSL directives, and sets up automatic renewal through a cron job or systemd timer. After Certbot configuration the Nginx configuration includes the certificate paths and SSL settings automatically.

Nginx performance optimisation

Nginx’s performance can be further optimised through configuration tuning, relevant for high-traffic redirect management infrastructure.

Worker process configuration:

worker_processes auto;        # Match CPU core count
worker_connections 1024;      # Connections per worker
use epoll;                    # Linux-optimised event mechanism
multi_accept on;              # Accept multiple connections per event
worker_processes auto;        # Match CPU core count
worker_connections 1024;      # Connections per worker
use epoll;                    # Linux-optimised event mechanism
multi_accept on;              # Accept multiple connections per event
worker_processes auto;        # Match CPU core count
worker_connections 1024;      # Connections per worker
use epoll;                    # Linux-optimised event mechanism
multi_accept on;              # Accept multiple connections per event

Gzip compression: reducing response bandwidth through compression:

gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 256;
gzip_comp_level

gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 256;
gzip_comp_level

gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 256;
gzip_comp_level

HTTP/2 support: enabling HTTP/2 for improved multiplexing:

server {
    listen 443 ssl http2;
    # http2 parameter enables HTTP/2

server {
    listen 443 ssl http2;
    # http2 parameter enables HTTP/2

server {
    listen 443 ssl http2;
    # http2 parameter enables HTTP/2

Caching static files: serving static assets with aggressive cache headers:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable"

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable"

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable"

Nginx vs Apache for redirects

Choosing between Nginx and Apache for redirect management depends on the hosting environment and performance requirements.

Performance: Nginx handles high concurrency with lower memory usage than Apache, important for redirect infrastructure handling high traffic volumes. For redirect-heavy workloads, domains receiving thousands of redirect requests per second, Nginx’s event-driven architecture provides better performance than Apache’s process-based model.

Configuration simplicity: Nginx redirect syntax, particularly the return directive, is simpler and more readable than Apache mod_rewrite syntax for common redirect patterns. Complex pattern-based redirects are slightly more complex in Nginx than Apache but remain manageable.

Shared hosting compatibility: Apache with.htaccess remains dominant in shared hosting environments where users do not have server configuration access. Nginx does not support.htaccess, all configuration requires server administrator access. For shared hosting environments Apache.htaccess is the only option.

Modern production infrastructure: Nginx dominates modern production infrastructure, VPS hosting, containerised deployments, cloud-native infrastructure. New production deployments should default to Nginx unless specific Apache requirements,.htaccess compatibility, specific modules, dictate otherwise.

Related terms

Related terms

Ready to keep every link alive?

Ready to keep every link alive?

Ready to keep every link alive?