URL rewriting is an indispensable technique for web developers and administrators. With over 30% market share among web servers, Nginx powers many of the world‘s busiest sites – making it‘s flexible rewrite module especially useful for traffic handling, security, and search optimization.
This comprehensive 4500+ word guide aims to make you an URL rewriting expert by exploring real-world examples, best practices, and the underlying Nginx architecture.
Why URL Rewriting Matters
Before diving into the Nginx configuration, let‘s motivate why URL rewriting is worth mastering:
Traffic Routing
Rewriting allows you to route requests based on factors like device, geolocation, A/B testing segments, and much more. This drives higher engagement and conversions.
For example, the following snippet routes mobile users to the responsive site:
if ($http_user_agent ~* "(Mobile)") {
rewrite ^ /mobile last;
}
Search Engine Optimization
URL rewriting enables search engine friendly structures proven to improve rankings. For instance, rewriting:
/index.php?product=blue-widget
to
/blue-widget
Or utilizing 301 permanent redirects when pages move.
Security & Access Control
Rewrites can restrict access based on IP, credentials, request methods, and block malicious requests. For example, blocking repeat offender IPs:
if ($bad_bot) {
return 403;
}
if ($http_user_agent ~*(bot|spy)) {
set $bad_bot 1;
}
The above demonstrates the flexibility of conditionals.
API Management
Route API endpoints to relevant servers, handle versioning e.g /v1/users, normalize URIs between microservices, and improved legacy compatibility.
Nginx Rewriting Under the Hood
To understand Nginx rewriting, you need to first comprehend a few key aspects of the Nginx architecture:

Requests enter Nginx and get routed through a series of modules that transform and handle them before proxying to an upstream.
The rewrite module sits early in this pipeline, analyzing the request URI and conditionally transforming it based on directives.
Other key modules like Try_Files attempts to serve files directly while Proxy_Pass forwards requests upstream.
Understanding this flow is vital for effective rule writing.
Performance Considerations
The rewrite module is incredibly fast – able to process tens of thousands of requests per second. However, complex rules with many regular expression conditionals can add up.
That‘s why it‘s critical to:
- Use redis or memcached to cache common rewrites
- Prefix match rather than complex regular expressions
- Return early from location blocks when possible
Getting Started with URL Rewriting
The primary directives for rewriting are return and rewrite:
return simply redirects the original request externally:
return 301 $scheme://www.mydomain.com$request_uri;
rewrite transforms the request URI internally using regular expressions:
rewrite ^/users/(\d+)/? /profile?id=$1 last;
Let‘s explore some common examples starting simple and getting more advanced.
1. Canonical Domain Redirect
A common task is to choose a single canonical domain and redirect alternate versions to it:
server {
listen 80;
server_name domain.com www.domain.com;
return 301 $scheme://www.domain.com$request_uri;
}
This returns a permanent redirect to www.domain.com using the captured $scheme and original $request_uri.
2. Force HTTPS Protocol
Similar to above, we can force HTTPS instead of HTTP:
server {
listen 80;
server_name domain.com;
return 301 https://$host$request_uri;
}
3. Static URL Rewriting
Nginx can also rewrite specific URI path matches:
location = /old/file.html {
rewrite ^/old/file.html /new/file.html permanent;
}
This rewrite from /old/file.html to /new/file.html uses a permanent redirect code for search engines.
4. Dynamic URL Rewriting
One of the most powerful features is enhancing dynamic URLs by extracting parameters for reuse:
location ~ ^/users/(\d+)/? {
rewrite ^/users/(\d+)/? /profile?id=$1 last;
}
Here the numeric user ID gets parsed from the original URL for building the rewritten profile URL.
Question – when would you want to use last vs permanent flags on rewrites?
Intermediate Rewriting
Now let‘s level up our techniques including variables, maps, and multipass processing.
Utilizing Maps
Maps create key/value storage for lookups – reducing duplicate rules.
For example:
map $http_host $canonical {
hostnames;
default 0;
domain.com www.domain.com;
foo.domain.com www.domain.com;
}
server {
listen 80;
server_name domain.com foo.domain.com;
if ($canonical) {
return 301 https://$canonical$request_uri;
}
}
This consolidates multiple domains to a single rewritten host.
Benefits? DRY, easier central management. Drawbacks? Added complexity.
Multi-Stage Rewrites
For more complex transformations, we can chain rewrite rules:
location /users {
rewrite ^/users/(\d+) /profile?id=$1 break;
rewrite ^/profile /account?view=profile last;
}
Here /users/123 gets extracted first, then rewritten again to /account.
Variables For Reuse
Captures parts can be stored in variables too:
set $username bob;
location /users {
rewrite ^/users/(\w+) /profile?name=$username last;
}
Later we could check $username for custom handling.
Dynamic Environments
Rules can also key off dynamic server variables:
server {
listen 80;
if ($hostname !~ server1) {
rewrite ^ http://server1.myhost.com$uri permanent;
}
}
This proxies requests to another origin when not already on server1.
Advanced Rewriting
Let‘s wrap up with some advanced examples and best practices for production rewriting.
Regex Optimization
Prefix matching with ^ is faster than unnecessary wildcards .:
location ~ ^/api/v1 { ... } # good
location ~ .*api.* { ... } # bad
Also avoid overusing $ end of string anchor when possible.
Security: Rate Limiting
Stop brute force attacks using conditionals to test rate limit counters with limit_req:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /login {
limit_req zone=mylimit burst=5 nodelay;
if ($nodelay) {
return 403 "Rate limit exceeded";
}
rewrite ^ /login.php last;
}
}
Microservices: Header Modification
Rewrite microservice endpoints and modify headers for routing:
location /users {
rewrite /users/(.*) /$1 break;
proxy_pass http://user-service;
proxy_set_header X-Type user-api;
}
Here the header X-Type lets the API server handle the style of requests.
Redirect Chains
When possible, use direct returns rather than chaining:
if ($suspicious) {
# Chained
rewrite ^ /threats?ref=$uri&http=$http_referer last;
# vs Direct
return 302 /threats?ref=$uri&http=$http_referer;
}
The direct return prevents processing additional rules.
Logging & Debugging
Finding errors in complex rewrites can be tricky – use the rewrite_log directive:
http {
rewrite_log on;
# rules here...
}
This logs processing for debugging which can be invaluable when things break.
Conclusion
URL rewriting encompasses a variety of critical functions from traffic handling to access controls and search optimization.
Mastering Nginx‘s flexible rewrite module empowers developers to craft redirects, protect sites, define APIs, and build highly scalable architecture – making it an essential component of any stack.
Let me know in the comments below any other use cases I should cover!


