In Nginx configuration, the ~
and ~*
operators perform regular expression matching with a crucial distinction:
# Case-sensitive matching (exact case required)
if ($uri ~ "^/admin/") {
return 403;
}
# Case-insensitive matching (any letter case accepted)
if ($uri ~* "^/admin/") {
return 403;
}
The underlying implementation uses PCRE (Perl Compatible Regular Expressions) with these flags:
~ → No special flags (case-sensitive by default)
~* → Adds PCRE_CASELESS flag
Case-insensitive matching generally has slightly higher CPU overhead due to:
- Additional character case conversions
- Larger potential match set
When to use ~
:
# Matching specific API endpoints
location ~ "^/api/v[0-9]/transactions" {
proxy_pass http://backend;
}
When to use ~*
:
# File extension matching (common with mixed-case extensions)
location ~* "\.(jpg|jpeg|png|gif)$" {
expires 30d;
}
# This won't match "/Admin" or "/ADMIN"
if ($http_host ~ "admin.example.com") {
# ...
}
# This will match all case variations
if ($http_host ~* "admin.example.com") {
# ...
}
Remember that Nginx variables like $host
are typically lowercase, while $http_*
variables preserve original case.
# Complex case-sensitive match with capture groups
if ($request_uri ~ "^/users/([0-9]+)/profile$") {
set $user_id $1;
}
# Case-insensitive domain whitelisting
if ($http_referer ~* "(www\.)?(example|foobar)\.(com|net)") {
# ...
}
In Nginx configuration, both ~
and ~*
are regex matching operators, but they handle case sensitivity differently:
# Case-sensitive match (only matches exact case)
if ($http_referer ~ www.Foobar.net) {
# matches "www.Foobar.net" but not "www.foobar.net" or "WWW.FOOBAR.NET"
}
# Case-insensitive match
if ($http_referer ~* www.foobar.net) {
# matches all variations: "www.foobar.net", "WWW.FOOBAR.NET", "www.FooBar.Net"
}
Use ~
when:
- You need exact case matching (security headers, sensitive paths)
- Matching case-sensitive APIs or protocols
Use ~*
when:
- Handling user-generated content (URLs, form inputs)
- Matching domain names or file extensions (.jpg, .JPG, .jPg)
Case-sensitive matches (~
) are generally slightly faster (5-15% in benchmarks) because they don't require case normalization. However, the difference is negligible for most use cases.
# Multiple domain matching (case-insensitive)
if ($host ~* "(example\.com|test\.org)") {
add_header X-Matched-Domain $host;
}
# File extension whitelisting
location ~* "\.(jpg|jpeg|png|gif)$" {
expires 30d;
}
# Case-sensitive API routing
location ~ "^/api/v[1-3]/" {
proxy_pass http://backend;
}
- Using
~*
for security checks (attackers may bypass with case variation) - Forgetting that
~
matches substrings unless anchored:# Without anchors (matches "evilfoobar.com") if ($host ~ foobar) { ... } # With anchors (matches exactly "foobar.com") if ($host ~ ^foobar\.com$) { ... }
- Always use
^
and$
anchors when matching entire strings - For security checks, prefer
~
with exact matches - Document which regex operator you're using in complex configurations