Nginx If Directive: Syntax, Examples, and Safe Usage

The Nginx if directive allows you to apply conditional logic within server and location blocks, enabling different responses based on request characteristics like IP addresses, user agents, request methods, or URI patterns. While this flexibility can solve specific problems that other directives cannot, if behaves differently from traditional programming conditionals and requires careful use to avoid unexpected results. By the end of this guide, you will understand the if directive syntax, know which operations work safely inside if blocks, and learn when to use alternatives like map or dedicated location blocks instead.

What Is the Nginx If Directive?

The if directive is part of the ngx_http_rewrite_module and evaluates conditions to determine whether to apply specific configuration directives. Unlike programming languages, Nginx has no else keyword. Instead, you achieve conditional branching through multiple if statements, the map directive, or separate location blocks.

The basic syntax places a condition in parentheses, followed by a block of directives:

if (condition) {
    # Directives to apply when condition is true
}

The if directive can appear inside server and location blocks, not at the http level. When the condition evaluates to true, Nginx applies the directives inside the block.

Condition Types

Nginx supports several condition types:

  • Variable check: A variable name alone evaluates as false if empty or exactly "0", otherwise true
  • String comparison: Use = and != for exact matches
  • Regular expression matching: Use ~ for case-sensitive and ~* for case-insensitive matching. Negative operators !~ and !~* are also available
  • File tests: Use -f and !-f for file existence, -d and !-d for directories, -e and !-e for any existence, -x and !-x for executable files

Safe Operations Inside If Blocks

The if directive has a well-documented reputation for unexpected behavior. This happens because if creates a nested configuration context, and some directives do not behave predictably when inherited across these contexts. Understanding which operations are safe prevents frustrating debugging sessions.

Directives That Work Reliably

The following directives are safe inside if blocks because they are part of the rewrite module and designed to work with conditional evaluation:

  • return: immediately returns a status code or redirect
  • rewrite: modifies the request URI
  • set: assigns values to variables
  • break: stops processing rewrite directives

Directives That May Behave Unexpectedly

These directives can produce surprising results inside if blocks due to configuration inheritance:

  • root: may not apply as expected when combined with other conditions
  • proxy_pass: can cause request handling issues
  • fastcgi_pass: similar inheritance problems as proxy_pass
  • add_header: headers may not appear in responses depending on where the if block terminates
  • try_files: should be used outside if blocks or replaced with alternative logic

When you need to change root, proxy_pass, or similar directives based on conditions, use the map directive or separate location blocks instead. This approach is more predictable and easier to maintain.

Practical Examples

The following examples demonstrate common use cases where the if directive is appropriate and safe.

Block Specific IP Addresses

To deny access from a specific IP address, use return inside the if block:

server {
    listen 80;
    server_name example.com;

    if ($remote_addr = "203.0.113.50") {
        return 403;
    }

    location / {
        root /var/www/html;
        index index.html;
    }
}

This configuration returns a 403 Forbidden response for requests originating from the specified IP. For blocking multiple IPs or ranges, consider using the geo or map directive instead, as they handle complex matching more efficiently.

Restrict HTTP Methods

Some applications need to restrict certain HTTP methods. The following example returns 405 Method Not Allowed for POST requests on a static content location:

location /static/ {
    if ($request_method = POST) {
        return 405;
    }

    root /var/www;
}

The $request_method variable contains the HTTP method (GET, POST, PUT, DELETE, etc.). Because return terminates request processing immediately, this pattern is safe.

Block Unwanted User Agents

You can identify crawlers or automated tools by their user agent strings and block access:

server {
    listen 80;
    server_name example.com;

    if ($http_user_agent ~* (curl|wget|python|scrapy)) {
        return 403;
    }

    location / {
        root /var/www/html;
    }
}

The ~* operator performs case-insensitive regular expression matching. This example blocks common command-line and scripting tools. For production environments, combine this approach with rate limiting for more comprehensive bot protection.

Force HTTPS Redirect

A common use case redirects HTTP traffic to HTTPS:

server {
    listen 80;
    server_name example.com;

    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }
}

While this works, the preferred approach uses a dedicated server block for HTTP that only handles the redirect:

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

server {
    listen 443 ssl;
    server_name example.com;
    # SSL configuration and main site content
}

The dedicated server block approach is clearer and avoids any if directive overhead. For more redirect patterns, see the Nginx URL redirect guide.

Rewrite URIs Conditionally

When you need to modify the request URI based on a condition, use rewrite inside the if block:

location / {
    if ($args ~ "legacy=true") {
        rewrite ^/app/(.*)$ /legacy/$1 last;
    }

    root /var/www/html;
}

This redirects requests containing legacy=true in the query string to a different path. The last flag tells Nginx to restart location matching with the new URI. For more complex rewrite patterns, see the Nginx rewrite rules guide.

Test and Apply Configuration Changes

After adding or modifying if directives, always validate the configuration before applying it. Syntax errors or logic mistakes can prevent Nginx from starting or cause unexpected behavior.

Test the configuration syntax:

sudo nginx -t

When the configuration is valid, you will see output confirming success:

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

If errors exist, the output identifies the file and line number where the problem occurs. After confirming the syntax is correct, reload Nginx to apply changes without dropping active connections:

sudo systemctl reload nginx

This command produces no output on success. To verify the reload succeeded, check the service status:

systemctl status nginx --no-pager

Verify Conditional Behavior

Test your conditions using curl to confirm they work as expected. For a user agent condition example:

curl -I -A "curl/8.0" http://example.com/

The -I flag requests headers only, and -A sets the user agent string. If your condition blocks curl, you should receive a 403 response:

HTTP/1.1 403 Forbidden
Server: nginx/1.x.x
Date: Mon, 06 Jan 2026 00:00:00 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive

For IP-based conditions, test from the restricted IP or use a proxy service to verify the block works correctly.

Alternatives to the If Directive

Many tasks commonly attempted with if are better handled by other Nginx features. These alternatives avoid the inheritance issues and are generally more efficient.

Use Map for Variable-Based Logic

The map directive evaluates conditions at configuration parse time and stores results in variables, making it efficient for complex conditional logic:

map $http_user_agent $is_bot {
    default         0;
    ~*curl          1;
    ~*wget          1;
    ~*python        1;
    ~*scrapy        1;
}

server {
    listen 80;
    server_name example.com;

    if ($is_bot) {
        return 403;
    }

    location / {
        root /var/www/html;
    }
}

This approach separates the condition logic from the action, making configurations easier to read and maintain. The map directive must appear in the http context, outside server blocks.

Use Location Blocks for Path-Based Logic

When behavior should differ based on the request path, use separate location blocks rather than if inside a single location:

location /api/ {
    # API-specific configuration
    proxy_pass http://backend;
}

location /static/ {
    # Static file serving
    root /var/www;
    expires 30d;
}

location / {
    # Default handling
    root /var/www/html;
}

Nginx evaluates location blocks efficiently, and this pattern is clearer than complex if statements checking the URI. For details on location matching, see the Nginx reverse proxy guide.

Use Try Files for Fallback Logic

The try_files directive handles file existence checks more reliably than if with -f tests:

location / {
    try_files $uri $uri/ /index.html;
}

This attempts to serve the requested file, then a directory index, then falls back to index.html. This pattern is essential for single-page applications and works without any conditional directives.

Troubleshooting Common Issues

Configuration Test Fails with Syntax Error

If nginx -t reports an error in your if directive, check these common mistakes:

  • Missing spaces: Nginx requires spaces around operators: if ($var = "value") not if ($var="value")
  • Unquoted strings with special characters: If your pattern contains } or ;, wrap the entire regular expression in quotes
  • Wrong context: The if directive cannot appear at the http level, only inside server or location blocks

Condition Does Not Match as Expected

When your condition appears correct but does not trigger:

Enable rewrite logging to see how Nginx evaluates conditions:

server {
    rewrite_log on;
    error_log /var/log/nginx/error.log notice;
    # ... rest of configuration
}

After reloading and making a test request, check the error log for condition evaluation details:

sudo tail -20 /var/log/nginx/error.log

The log shows which conditions are evaluated and their results, helping identify why matching fails.

Rewrite Loops Causing 500 Errors

If Nginx returns 500 Internal Server Error after adding rewrite rules, check for infinite loops. Nginx limits rewrites to 10 cycles per request. The error log will show:

rewrite or internal redirection cycle while processing "/path"

To fix this, use the break flag instead of last when the rewrite should not trigger additional location matching, or add conditions to prevent the rewrite from matching again after execution.

Headers Not Appearing in Response

If add_header inside an if block does not work, the directive inheritance model is likely the cause. Headers set inside if blocks only appear when no other add_header directives exist at the outer level. To resolve this, either add all headers inside the if block or use the map directive to set header values conditionally:

map $scheme $hsts_header {
    https   "max-age=31536000; includeSubDomains";
    default "";
}

server {
    add_header Strict-Transport-Security $hsts_header always;
    # ... other configuration
}

This approach adds the HSTS header only for HTTPS connections without using if. For more header configuration examples, see the Nginx security headers guide.

Best Practices

Following these guidelines helps you use the if directive effectively while avoiding common problems:

  • Use if sparingly: The if directive adds complexity and can cause unexpected behavior. Before adding an if block, check whether map, try_files, or separate location blocks solve the problem more cleanly
  • Stick to safe operations: Only use return, rewrite, set, and break inside if blocks. These directives are designed to work with conditional evaluation and behave predictably
  • Test conditions explicitly: Use curl or browser developer tools to verify conditions trigger as expected. Do not assume the condition works based on syntax alone
  • Document complex logic: Add comments explaining why each if block exists and what condition it handles. This helps future maintainers understand the configuration
  • Use staging environments: Test configuration changes in a non-production environment before deploying. Conditional logic bugs can affect all traffic matching the condition

Conclusion

The Nginx if directive provides conditional configuration capabilities for tasks like blocking IPs, restricting request methods, and redirecting based on request properties. Because Nginx lacks an else keyword and if creates nested configuration contexts, stick to safe operations such as return and rewrite. For complex conditional logic, the map directive offers better maintainability, while separate location blocks handle path-based routing more predictably. Always test changes with nginx -t before reloading, and verify conditional behavior with tools like curl to confirm your configuration works as intended.

Leave a Comment

Let us know you are human: