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
-fand!-ffor file existence,-dand!-dfor directories,-eand!-efor any existence,-xand!-xfor 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 redirectrewrite: modifies the request URIset: assigns values to variablesbreak: 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 conditionsproxy_pass: can cause request handling issuesfastcgi_pass: similar inheritance problems as proxy_passadd_header: headers may not appear in responses depending on where theifblock terminatestry_files: should be used outsideifblocks or replaced with alternative logic
When you need to change
root,proxy_pass, or similar directives based on conditions, use themapdirective or separatelocationblocks 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")notif ($var="value") - Unquoted strings with special characters: If your pattern contains
}or;, wrap the entire regular expression in quotes - Wrong context: The
ifdirective cannot appear at thehttplevel, only insideserverorlocationblocks
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
ifdirective adds complexity and can cause unexpected behavior. Before adding anifblock, check whethermap,try_files, or separatelocationblocks solve the problem more cleanly - Stick to safe operations: Only use
return,rewrite,set, andbreakinsideifblocks. These directives are designed to work with conditional evaluation and behave predictably - Test conditions explicitly: Use
curlor 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
ifblock 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.