Nginx If Else Logic: Safe If, Map, AND/OR Patterns

Understand Nginx if else logic, safe if usage, map alternatives, AND/OR workarounds, nested-if errors, and curl checks.

Last updatedAuthorJoshua JamesRead time10 minGuide typeNginx

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 tryNative supportSupported patternWhy
else or else ifNomap with default, separate location blocks, or separate server blocksNginx has no fallback branch inside if.
andNoBuild a composite map key such as "$request_method:$http_origin".The if directive accepts one condition.
orNoUse regex alternation in one condition or a map row.Regex alternatives such as (app|admin) are normal Nginx syntax.
notNo keywordUse !=, !~, !~*, !-f, !-d, !-e, or !-x.Nginx provides negated operators, not a separate boolean word.
Nested ifNoMove 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 patternContextUse it forNotes
ifserver, locationOne conditional check in the rewrite moduleNo native else, else if, and, or or; conditions remain limited to one documented check.
returnserver, location, ifStatus codes and redirectsLow-risk inside if because it stops request processing immediately.
rewrite and breakserver, location, ifURI rewrites handled by the rewrite moduleUse break or last deliberately; repeated rewrites are capped at 10 cycles.
setserver, location, ifAssigning request-time helper variablesUseful for simple flags, but map is usually clearer for multi-branch logic.
maphttpElse-style branching, AND-style composite checks, and OR-style regex alternativesThe map directive creates variables whose values depend on other variables and evaluates them only when used.
try_filesserver, locationFile and fallback routingThe try_files directive is cleaner than testing file existence with if.
proxy_passlocation, if in location, limit_exceptReverse proxy routingThe 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 as if ($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, separate location blocks, or a dedicated server block. Keep if for 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 if sparingly: Start with map, try_files, or location blocks when the condition changes routing or fallback behavior.
  • Prefer terminating actions: return is the simplest safe action inside if because 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 -t proves syntax, while curl, 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.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show our tutorials more often in Top Stories and mark them as preferred in AI Mode and AI Overviews when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="https://example.com">link</a> link
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Verify before posting: