Nginx conditionals become tricky when a configuration needs an else branch. The rewrite module provides if, but it does not provide else, else if, and, or or operators. In Nginx, if else logic usually means choosing between a safe single-condition action, a map variable, or separate location blocks.
Use the Nginx if directive for narrow request-time decisions such as returning a status code, redirecting, rewriting a URI, or setting a helper variable. Use map, try_files, or normal routing blocks when the condition controls upstream selection, file fallback, headers, or larger site behavior.
Understand Nginx If Else Logic
The if directive belongs to the Nginx rewrite module. What many admins call an Nginx if statement is this directive, not a full programming-language statement. It evaluates one documented condition inside a server or location context, then runs the directives in its block when that condition is true. Because Nginx assigns the request to the configuration created for that if block, inherited directives can behave differently from ordinary programming-language conditionals.
The basic syntax uses a condition in parentheses followed by a directive block:
if (condition) {
# Directives to apply when condition is true
}
The most important point is what the syntax does not include. Nginx does not have a native else branch, and an if condition does not accept boolean and or or operators. Treat unsupported syntax as a design signal before adding conditional logic.
Can Nginx Use Else, Else If, AND, OR, or NOT?
Nginx config has no native else or else if keyword. Treat those searches as a routing question: use map when the branch should produce a variable, use location or try_files when Nginx already has a routing directive, and use if only for the final small action.
| Syntax readers try | Native support | Supported pattern | Why |
|---|---|---|---|
else or else if | No | map with default, separate location blocks, or separate server blocks | Nginx has no fallback branch inside if. |
and | No | Build a composite map key such as "$request_method:$http_origin". | The if directive accepts one condition. |
or | No | Use regex alternation in one condition or a map row. | Regex alternatives such as (app|admin) are normal Nginx syntax. |
not | No keyword | Use !=, !~, !~*, !-f, !-d, !-e, or !-x. | Nginx provides negated operators, not a separate boolean word. |
Nested if | No | Move the combined decision into map or split the request path into locations. | if is valid only in server or location context. |
After choosing a supported pattern, check the directive context before placing it in a file. Nginx rejects directives in unsupported blocks, and some directives inherit settings differently when they appear inside conditional configuration.
| Directive or pattern | Context | Use it for | Notes |
|---|---|---|---|
if | server, location | One conditional check in the rewrite module | No native else, else if, and, or or; conditions remain limited to one documented check. |
return | server, location, if | Status codes and redirects | Low-risk inside if because it stops request processing immediately. |
rewrite and break | server, location, if | URI rewrites handled by the rewrite module | Use break or last deliberately; repeated rewrites are capped at 10 cycles. |
set | server, location, if | Assigning request-time helper variables | Useful for simple flags, but map is usually clearer for multi-branch logic. |
map | http | Else-style branching, AND-style composite checks, and OR-style regex alternatives | The map directive creates variables whose values depend on other variables and evaluates them only when used. |
try_files | server, location | File and fallback routing | The try_files directive is cleaner than testing file existence with if. |
proxy_pass | location, if in location, limit_except | Reverse proxy routing | The proxy_pass directive has a documented if in location context, but it is still a poor replacement for real branching. |
Nginx If Condition Types
Nginx documents these condition forms for if:
- Variable truth check: A variable is false when its value is empty or exactly
0; otherwise it is true. - String comparison: Use
=and!=for exact matches, such asif ($request_method = OPTIONS). - Regular expression matching: Use
~for case-sensitive matching,~*for case-insensitive matching, and!~or!~*for negative matches. - File tests: Use
-f,-d,-e, and-x, plus their negative forms, to test files, directories, existing paths, and executable files.
Plain tokens such as OPTIONS do not need quotes in a string comparison. Quote complex strings when it makes parsing clearer, and quote regular expressions that contain } or ; because those characters can confuse the Nginx configuration parser.
Use Negated Operators Instead of Nginx If Not
Nginx does not have a not keyword for if conditions. Use the negated operator that matches the test: != for string mismatch, !~ or !~* for regex mismatch, and !-f, !-d, !-e, or !-x for negative file checks.
For a small terminating rule, a negative regex condition can reject requests outside known public paths:
location / {
if ($request_uri !~ ^/(public|assets)/) {
return 403;
}
root /var/www/html;
}
Use a separate location, access-control directive, or application authorization check when the rule is more than a simple rejection. A negative if can answer one request-time question, but it should not become the main routing design for a site.
Safe Directives Inside Nginx If Blocks
The lowest-risk if bodies use rewrite-module directives: return, rewrite, set, and break. Other directives may parse in specific if in location contexts, but they can be harder to reason about because the if block creates its own configuration context.
The common warning that Nginx if is dangerous is a placement warning, not a ban on every conditional. A terminating return is easy to audit; a conditional that changes routing, proxy behavior, inherited headers, or file fallback should usually move out of if.
When the condition changes where a request is served, which upstream receives it, or which headers apply, prefer
map, separatelocationblocks, or a dedicated server block. Keepiffor small actions that terminate or adjust rewrite processing.
Practical Nginx If Directive Examples
Use these patterns when one condition leads to one small action. Each snippet belongs inside the broader Nginx configuration for the relevant site, not as a standalone replacement for a complete virtual host.
Return 403 for a Single IP Address
For a one-off block, return inside if is direct and safe:
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 returns a 403 response before the request reaches the main content location. For multiple IPs, CIDR ranges, or a reusable access policy, use geo, map, allow, and deny instead of stacking many if blocks.
Match OPTIONS Requests with Nginx If Syntax
The request-method comparison syntax is if ($request_method = OPTIONS). The value OPTIONS is a plain token, so quotes are optional for this exact comparison. Use uppercase method names for normal browser and API requests; options is a different string and will not match a standard preflight request that arrives as OPTIONS.
location /api/ {
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://127.0.0.1:3000;
}
This pattern handles the method check with return and leaves the normal proxy configuration outside the if block. If the OPTIONS response also needs CORS headers, define those headers at the same level as the rest of the location or use a map-driven variable so header inheritance stays predictable. Use the configure CORS in Nginx workflow for preflight headers, allowlists, and response verification.
Block Unwanted User Agents
A regular expression condition can match a user agent and return a response immediately:
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 regex matching. User-agent blocking is easy to bypass, so production bot controls usually work better when paired with configure Nginx rate limiting and application-side abuse checks.
Redirect HTTP to HTTPS Without If
An HTTP-to-HTTPS redirect can be written with if, but a dedicated HTTP server block is clearer and avoids conditional work on every request.
server {
listen 80;
server_name example.com;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
}
Prefer this version when the whole port-80 server exists only to redirect traffic:
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
}
Dedicated redirect blocks scale better when you also need canonical host redirects. For canonical host, path, and scheme changes, use the redirect URLs in Nginx workflow.
Rewrite URIs Conditionally
Use rewrite inside if only when the condition truly belongs to rewrite processing:
location /app/ {
if ($arg_legacy = "true") {
rewrite ^/app/(.*)$ /legacy/$1 last;
}
root /var/www/html;
}
The $arg_legacy variable reads the legacy query parameter. The last flag restarts location matching after the URI changes, which is useful when the rewritten path should use a different location. For broader rewrite design, build from the create Nginx rewrite rules workflow.
Advanced Nginx Conditional Patterns Without Else
When a condition starts to look like if, else if, and else, move the decision into a variable or a separate routing block. The Nginx configuration becomes easier to test because the branching rule has one owner.
Use Map for Nginx If Else and AND Logic
The map directive belongs in the http context and creates a variable from one or more source variables. A composite source string handles AND-style checks, while regex alternation handles OR-style choices.
map "$request_method:$http_origin" $allowed_preflight {
default 0;
"~^OPTIONS:https://(app|admin)\.example\.com$" 1;
}
server {
listen 80;
server_name example.com;
location /api/ {
if ($allowed_preflight) {
return 204;
}
proxy_pass http://127.0.0.1:3000;
}
}
This reads as “method is OPTIONS and origin is one of the allowed hosts” without trying to force and into the if condition. The default 0 row is the else-style fallback.
Use Location Blocks for Path-Based Conditions
When different paths need different behavior, let Nginx choose the location instead of checking $uri inside one block:
location /api/ {
proxy_pass http://127.0.0.1:3000;
}
location /static/ {
root /var/www;
expires 30d;
}
location / {
root /var/www/html;
}
Nginx location selection is built for this job and is easier to audit than URI conditions scattered through a single location. If the path-based rule forwards traffic to an application, the create an Nginx reverse proxy workflow covers standard proxy headers and upstream layout.
Use Try Files for File Fallback Conditions
File checks are valid if conditions, but try_files is the normal Nginx pattern for static-file and application fallback routing:
location / {
try_files $uri $uri/ /index.html;
}
Nginx checks the requested file, then a directory path, then internally redirects to /index.html. This keeps file-existence logic in one directive and avoids a fragile if (!-f ...) branch.
Keep Proxy Pass Outside If Branches
The official proxy_pass documentation lists if in location as a valid context, so the directive is not simply “forbidden” there. It is still the wrong tool for most else-style routing because URI replacement, variable upstreams, headers, and caching rules become harder to predict inside conditional blocks.
Use if to return or rewrite before the request reaches the proxy, then keep proxy_pass in the location that owns upstream behavior. If the condition chooses between backends, design that as separate locations, named locations, or a tested map-driven upstream strategy rather than hiding the routing decision inside a nested if.
Test and Apply Nginx If Directive Changes
Validate every conditional change before reloading Nginx. A missing semicolon, invalid context, or rewrite loop can prevent a site from serving traffic.
sudo nginx -t
A valid configuration returns these success lines:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload the service only after the syntax test passes:
sudo systemctl reload nginx
Confirm the service remains active:
systemctl is-active nginx
active
Verify Conditional Behavior with Curl
Use the curl command in Linux to test the response path that your condition should trigger. For the user-agent block example, send a matching user agent to the local Nginx listener and keep the intended virtual host in the Host header:
curl --noproxy '*' -sSI -A "curl/8.0" -H "Host: example.com" http://127.0.0.1/
A successful denial starts with this status line:
HTTP/1.1 403 Forbidden
For IP-based conditions, test from the restricted address or a controlled proxy path. Do not treat a local request from an allowed IP as proof that the denial rule works.
Nginx If Directive Best Practices
- Use
ifsparingly: Start withmap,try_files, orlocationblocks when the condition changes routing or fallback behavior. - Prefer terminating actions:
returnis the simplest safe action insideifbecause it ends processing immediately. - Keep proxy routing visible: Put
proxy_pass, proxy headers, and cache rules in the location that owns the upstream behavior. - Test the exact path:
nginx -tproves syntax, whilecurl, logs, and controlled client requests prove behavior. - Document unusual conditions: Comments help future maintainers understand why the conditional exists and why a simpler directive was not enough.
Troubleshoot Common Nginx If Directive Issues
Nginx If Directive Is Not Allowed Here
If the syntax test reports that if is not allowed, the directive is probably sitting in the wrong context:
sudo nginx -t
nginx: [emerg] "if" directive is not allowed here
Move the if block into a server or location block, then rerun the syntax test:
sudo nginx -t
Nginx If Condition Has Invalid Syntax
Missing spaces around the comparison operator can make Nginx parse the whole expression as a variable name instead of a comparison:
sudo nginx -t
nginx: [emerg] unknown "request_method=options" variable
Write the comparison with spaces around the operator:
if ($request_method = OPTIONS) {
return 204;
}
If the condition uses a regex containing } or ;, quote the whole regex so the parser does not treat those characters as block syntax.
Nginx If AND and OR Conditions Do Not Work
Nginx if conditions do not support boolean and or or operators. A condition that tries to combine comparisons fails during the syntax test:
sudo nginx -t
nginx: [emerg] invalid condition "$request_method"
Create a map variable from a composite key, then test that single variable inside if:
map "$request_method:$http_user_agent" $blocked_post_bot {
default 0;
"~^POST:.*(curl|python)" 1;
}
server {
listen 80;
server_name example.com;
if ($blocked_post_bot) {
return 403;
}
}
Retest the parser before applying the change:
sudo nginx -t
An empty Nginx if block is also a maintenance smell. It does not create an else branch or a placeholder condition; remove it or replace it with the actual return, rewrite, set, or break action.
Nginx Nested If Directive Is Not Allowed
Nginx does not allow an if block inside another if block. The syntax test reports the nested directive as an invalid context:
sudo nginx -t
nginx: [emerg] "if" directive is not allowed here
Move the combined decision into map, then keep the final if as one simple action:
map "$arg_a:$arg_b" $block_request {
default 0;
"1:1" 1;
}
server {
listen 80;
server_name example.com;
if ($block_request) {
return 403;
}
}
Run sudo nginx -t after replacing nested blocks, then reload Nginx only when the syntax test succeeds.
Nginx Rewrite Loops Return 500 Errors
A rewrite loop can happen when a rewritten URI matches the same rule again. Nginx stops after 10 internal rewrite cycles, and the error log shows the loop:
sudo tail -20 /var/log/nginx/error.log
rewrite or internal redirection cycle while processing "/path"
Use break when the request should continue in the current location, or tighten the regex so the rewritten URI no longer matches the same rule. After changing the rule, run sudo nginx -t, reload Nginx, and repeat the request that caused the loop.
Nginx If Condition Does Not Match
Enable rewrite logging temporarily when a condition looks correct but never triggers:
server {
rewrite_log on;
error_log /var/log/nginx/error.log notice;
# Rest of the server configuration
}
After reloading Nginx and making a test request, inspect the recent error log lines with the tail command in Linux:
sudo tail -20 /var/log/nginx/error.log
Relevant log fragments include:
[notice] [pid] *[request] "^/app/(.*)$" matches "/app/report" [notice] [pid] *[request] rewritten data: "/legacy/report", args: "legacy=true"
Disable rewrite_log after debugging because it can add noise to busy error logs.
Nginx Headers Do Not Appear After If Logic
The add_header directive has its own inheritance model: headers inherit from a previous level only when the current level defines no add_header directives. If conditional header logic produces missing or duplicate headers, move the decision into map and keep the header directive at one predictable configuration level.
map $request_uri $robots_header {
default "index, follow";
~^/private/ "noindex";
}
server {
add_header X-Robots-Tag $robots_header always;
}
Response headers can affect every site behind the same server, CDN, or proxy layer, so keep their ownership explicit. The configure Nginx security headers workflow covers header scope, reload checks, and response verification.
Conclusion
Nginx conditional logic stays predictable when if handles narrow rewrite-module actions and broader branching moves into map, location, and try_files. Keep return, rewrite, set, and break as the default tools inside if, then move routing, proxy selection, file fallback, and header policy into the directive that owns that behavior.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><a href="https://example.com">link</a><blockquote>quote</blockquote>