You are on page 1of 14

HTTP Rewrite Rule

Table of Contents
1. Introduction .....................................................................................................................................1
2. Nginx HTTP rewrite functionality....................................................................................................1
3. Rewrite examples.............................................................................................................................7
4. Rewrite rule best practices..............................................................................................................11
5. Apache rules to Nginx rules conversion.........................................................................................12
7. Dependencies..................................................................................................................................15
8. References......................................................................................................................................15
1. Introduction
More often than not there are times when your initial thought process changes mid way through
your production ready application and you decide to change your application url. It could be your
scheme (from a non-www to a www or vice-versa) or it could be your protocol (say, from http to
https) or it could be the pages moved from one directory to another directory or even the change of
domain name or even at times it is required to perorm a URL rewrite in order to map a nonexisting /foo context to the actual /bar one. For example :
UC 1 (non-WWW to WWW conversion) :
http://site.com/some-page-title.html to go to www.site.com/some-page-title.html
UC 2 (http to https conversion):
http://test.com to go to https://test.com
UC 3 :
http://www.example.com/products/1234 to go to http://www.example.com/show?product= 1234
UC 4:
http://example.com/issue1 --> http://example.com/shop/issues/custom_issue_name1
http://example.com/issue2 --> http://example.com/shop/issues/custom_issue_name2
http://example.com/issue3 --> http://example.com/shop/issues/custom_issue_name3
UC 5:
http://www.abc.com to go to "http://partner.abc.com/xyz"
UC 6:
Perform the URL rewrite based on the headers in the request. For Example suppose a request
www.abc.com with Accept-Language header as German (ge) is received, ane this the URL needs
to be rewritten to www.abc.com/ge in order to serve a German web page instead of the default
English page
2. Nginx HTTP rewrite functionality
The URL Rewriting and Redirection functionality of Nginx is being provided by the HttpRewrite
module. This module makes it possible to change URI using regular expressions (PCRE), and to
redirect and select configuration depending on variables. NGINX's rewrite module is a simple
regular expression matcher combined with a virtual stack machine.

The rewrite syntax:


rewrite regex replacement [ flag ]
Example:
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
The first part of any rewrite rule is a regular expression. As such, it is possible to use parentheses to
define certain parts as "captures", which can later be referenced by positional variables. A positional
variable is one in which its value depends on the order of the capture in the regular expression.They
are labeled by number, so positional variable $1 references what is matched by the first set of
parentheses, $2 the second set, and so on. For example, let us look at the the following regular
expression:
^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$
In the example above, the first positional variable, $1, references a two-letter string which comes
immediately after the string /images/ at the beginning of the URI. The second positional variable,
$2, refers to a five character string composed of lowercase letters and the numbers from 0 to 9. The
third positional variable, $3, is presumably the name of a file. And the last variable to be extracted
from this regular expression, $4, is one of png, jpg, or gif, which appears at the very end of the URI.
The second part of a rewrite rule is the URI to which the request is rewritten. If the specified regular
expression matches a URI, the URI is changed as specified in the replacement string. The rewrite
directives are executed sequentially in order of their appearance in the configuration file. It is also
possible to terminate further processing of the directives using flags (described later). If a
replacement string starts with http:// or https://, the processing stops and the redirect is returned
to a client. If this URI does matched with any of the other locations in the NGINX configuration,
then it is returned to the client in the Location header with either a 301 (Moved Permanently) or a
302 (Moved temporarily) HTTP status code indicating the type of redirect that is to be performed.
This status code may be specified explicitly if permanent or redirect is the third parameter.
If the directives of this module are given at the server level, then they are carried out before the
location of the request is determined. If in that selected location there are further rewrite directives,
then they also are carried out. If the URI changed as a result of the execution of directives inside
location, then location is again determined for the new URI.
This cycle can be repeated up to 10 times, after which Nginx returns a 500 error.
This third parameter to the rewrite rule is the flag. The flag can be any of the following:
1. last - completes processing of current rewrite directives and restarts the process (including
rewriting), i.e it stops processing the current set of ngx_http_rewrite_module directives and
starts a search for a new location matching the changed URI from all available locations. In
brief the last flag will cause NGINX to search for another location matching the rewritten
URI.
2. break - completes processing of current rewrite directives and non-rewrite processing
continues within the current block only.
3. permanent - returns permanent redirect with code 301

4. redirect - returns a temporary redirect with the 302 code. It means that a page has moved
temporarily to a new location. It is used if a replacement string does not start with http://
or https://. This code is not recommended for domain change.
Let us understand the differnece between last and break
location /video_test {
rewrite ^/video_test /video/ break;
}
In the above configuration since we have used the break flag with rewrite, it stops the rewrite
there and tries to find an upstream server with the changed URI in that section. As we don't have
one an 404 error message is returned to the client. The correct syntax would be to use last, if we
want Nginx to find the correct upstream withe the changed URI or use return or we can specify
where the request to be redirected to in that location section.
The permanent flag redirect URL with http status code 301. It means that a page has permanently
moved to a new location. This code is recommended by search engine giant Google:
The 301 redirects are particularly useful in the following circumstances:
1. You've moved your site to a new domain, and you want to make the transition as seamless as
possible.
2. People access your site through several different URLs. If, for example, your home page can
be reached in multiple ways - for instance, http://example.com/home,
http://home.example.com, or http://www.example.com - it's a good idea to pick one of those
URLs as your preferred (canonical) destination, and use 301 redirects to send traffic from
the other URLs to your preferred URL. You can also use Webmaster Tools to set your
preferred domain.
3. You're merging two websites and want to make sure that links to outdated URLs are
redirected to the correct pages.
There exists another directive known as return which stops the processing and return the
specified code to the client.
Syntax: return code [text];
return code URL;
return URL;
context: server, location, if
The return directive may indicate a status code, a status code with some text, or a status code with a
URI. If a bare URI is the sole parameter, then the status code is understood to be a 302. When the
text is placed after the status code, that text becomes the body of the response. If a URI is used
instead, then that URI becomes the value of the location header, to which the client will then be
redirected.
As a special case, a redirect URL can be specified as a URI local to this server, in which case the
full redirect URL is formed according to the request scheme ($scheme) and the
server_name_in_redirect and port_in_redirect directives. In addition, a URL for temporary

redirect with the code 302 can be specified as the sole parameter. Such a parameter should start with
the http://, https://, or $scheme string. A URL can contain variables.
When you use http:// or https:// with rewrite or use return then client gets the response and
tries to the new URL in this case the changed URL will become visible on the client browser. When
rewrite modifies the URL and forwards the request to the client then client will not see the changed
URL on its browser.
The comparison between rewrite and return rewrite

Only the part of the original url that


matches the regex is rewritten.
Slower than a return.
Returns HTTP 302 (Moved Temporarily)
in all cases, irrespective of permanent.
Suitable for temporary url changes.

Example:
rewrite ^ https://$server_name$request_uri?
permanent;

return

The entire url is rewritten to the url


specified.
Faster response than rewrite as there is
no regular expression matching
Returns HTTP 301 (Moved
Permanently).
Suitable for permanent changes to the
url.
No need to set permanent.

Example:
return 301 https://
$server_name$request_uri;

The following directive can be used to enable or disable logging of ngx_http_rewrite_module


module directives processing results into the error log at the notice level.
Syntax: rewrite_log on | off;
Default: off
Context: http, server, location, if
This module also provides another directive called set for creating a new variable and setting its
value. A value can contain text, variables, and their combination. This is useful in a number of
ways, from creating flags when certain conditions are present, to passing named arguments on to
other locations and logging what was done.
Syntax: set variable value;
Default:
Context: server, location, if
Few things to consider while writing the rewrite rules :

What pattern(s) do I have in my URLs?


Is there more than one way to reach a particular page?
Do I want to capture any parts of the URL into variables?
Am I redirecting to a site not on this server, or could my rule be seen again?
Do I want to replace the query string arguments?

If a replacement string includes the new request arguments, the previous request arguments are
appended after them. If this is undesired, putting a question mark at the end of a replacement string
avoids having them appended, for example :
rewrite ^/users/(.*)$ /show?user=$1 last;
The above command will map http://www.example.com/users/1234/abcxyz to
http://www.example.com/show?user=1234/abcxyz
whereas
rewrite ^/users/(.*)$ /show?user=$1? last;
the above command will map http://www.example.com/users/1234/abcxyz to
http://www.example.com/show?user=1234
Let us look at more directive called if directive
Syntax: if (condition) { . . . }
Default:
Context: server, location
The if directive evaluates the specified condition. If true, the directives of this module specified
inside the braces are executed, and a request is assigned the configuration inside the if directive.
Configurations inside the if directives are inherited from the previous configuration level.
The condition may be any of the following:

a variable name: false if empty or any string starting with 0


string comparison: using the = and != operators
regular expression matching: using the ~ (case-sensitive) and the ~*(case-insensitive)
positive operators and their negative counterparts !~ and !~*
file existence: using the -f and ! -f operators
directory existence: using the -d and ! -d operators
file, directory, or symbolic link existence: using the -e and ! -e operators
file executability: using the -x and ! -x operators

if ( $ http_ user_agent ~ MSIE ) {


rewrite ^(.*) $ /msie/$1 break ;
}
if ( $http_cookie ~* " id =([^;]+) (?:;| $ ) ") {
set $id $1 ;
}
if ( $request _method = POST ) {
return 405;

}
if ( $slow ) {
limit_rate 10 k ;
}
if ( $ invalid_ referer ) {
return 403;
}
3. Rewrite examples
Example 1: www to non-www redirect
For new websites, having the www before your domain is really not needed. Here is how to redirect
the www version of your website to the cleaner, non-www version.
server {
server_name www.domain.com;
return 301 $scheme://domain.com$request_uri;
}
server {
server_name domain.com;
......
}
Example 2: non-www to www redirect

Non WWW to WWW redirect


Nginx syntax
server
{
server_name domain.com;
return 301
$scheme://www.domain.com$request_uri;
}

Irule syntax
when HTTP_REQUEST {
if { [HTTP::host] eq "domain.com" } {
HTTP::redirect
http://www.domain.com[HTTP::uri]
}
}

Example 3: URL redirect


(i)
http://www.example.com/1234 --> http://www.example.com/v.php?id=1234

location ~ /[0-9]+ {
rewrite "/([0-9]+)" /v.php?id=$1? last;
}
(ii)
http://example.com/issue1 --> http://example.com/shop/issues/custom_issue_name1
http://example.com/issue2 --> http://example.com/shop/issues/custom_issue_name2
http://example.com/issue3 --> http://example.com/shop/issues/custom_issue_name3
location /issue {
rewrite ^/issue(.*) /shop/issues/ustom_issue_name$1 last;
}
If you want to the return the changed URI instead of forwarding, do this :
location /issue {
rewrite ^/issue(.*) http://$server_name/shop/issues/custom_issue_name$1 permanent;
}

URL redirect (www.mycompany.com/bus/texas to go to


www.mycompany.com/greyhound/searchBus.do?stationName=texas)
Nginx syntax

Irule syntax

location /bus {
rewrite ^/bus(.*) /greyhound/searchBus.do?
stationName=$1? last;
}

when HTTP_REQUEST {
set uri [HTTP::uri]
switch -glob [string tolower [HTTP::uri] ] {
"/bus/*" {
HTTP::uri "/greyhound/searchBus.do?
stationName=[string range $uri 5 end]"
}
}
}

Example 4: Handing domain name change


I have changed the domain name from long-domain-name-example.com to example.com which is
shorter and easy to remember. How do I redirect old domain to new domain with "HTTP/1.1 301
Moved Permanently" status code under nginx web server running on Unix like operating systems

From

To

http://long-domain-name-example.com

http://www.example.com

http://long-domain-name-example.com/file.html http://www.example.com/file.html

http://long-domain-name-example.com/cgihttp://www.example.com/cgi-bin/register.cgi?
bin/register.cgi?action=1file easier to read. This action=1
approach decreases nginx processing requi
http://long-domain-nameexample.com/static/js/lib.js?v=2

http://www.example.com/static/js/lib.js?v=2

server {
listen
75.126.153.206:80;
server_name long-domain-name-example.com ;
root
/usr/local/nginx/html;
index
index.html;
rewrite ^ $scheme://www.example.com $request_uri permanent;
# ....
}
Example 5: Redirect to a different domain with nginx
From:
example.org
example.org/foo.html
example.org/show.php?arg=value
example.org/dir1/dir2/bar.cgi

To:
example.com
example.com/foo.html
example.com/show.php?arg=value
example.com//dir1/dir2/bar.cgi

server {
server_name .example.org;
return 301 $scheme://example.com$request_uri;
}
server {
server_name example.com;Enables or disables logging of ngx_http_rewrite_module module
directives processing results into the error log at the notice level.
# rest of config, if any goes below ...
}
Example 6: Redirecting all the HTTP traffic to HTTPS
HTTP to HTTPS redirect
Nginx syntax
server {
listen *:80;
return 301 https://$host$request_uri ;
}

Irule syntax
when HTTP_REQUEST {
HTTP::respond 301 location "https://
[HTTP::host][HTTP::uri]"
}

The following is the complete configuration of nginx in order to redirect all the HTTP traffic to
HTTPS traffic

upstream backend {
server 127.0.0.1:8080;
}
server {
listen *:80;
return 301 https://$host$request_uri permanent;
}
server {
listen *:443;
ssl on;
ssl_protocols SSLv3 TLSv1;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/cert.key;
location / {
proxy_pass http://backend;
}
}
P.S. Make sure you have the certificates cd /etc/nginx
mkdir ssl
openssl req -new -x509 -days 9999 -nodes \
-out ssl/cert.pem -keyout ssl/cert.key
chown -R www-data:www-data ssl
chmod -R 700 ssl
If your application needs http to https redirect only for certain uris like for example, sign-in or signup then do the following server {
listen 80;
server_name http://www.example.com;
root /path-to-public-folder-of-project;
passenger_enabled on;
location ~ ^/(sign-up|sign-in) {
return 301 https://$host$request_uri;
}
}
Example 7: Redirecting based on the HTTP user agent

HTTP header based redirect


Nginx syntax

Irule syntax

if ($http_user_agent ~* ^Mozilla) {
rewrite ^/$ /homepage.max.html break;
}

when HTTP_REQUEST {
if { [HTTP::header "User-Agent"] contains
"iPhone"] } {
HTTP::redirect "http://m.mysite.com" }
}

4. Rewrite rule best practices


1. Avoid the following code :
rewrite ^ http://domain.com$request_uri? permanent;
rather use
return 301 http://domain.com$request_uri;
In the case of return we will not evauate any regular expression.
2. Using if to ensure a file exists is horrible. It's mean. If you have any recent version of Nginx
you should look at try_files which just made life much easier.
The following code is considerd as BAD:
server {
root /var/www/domain.com;
location / {
if (!-f $request_filename) {
break;
}
}
}
Use the following syntax otherwise :
server {
root /var/www/domain.com;
location / {
try_files $uri $uri/ /index.html;
}
}
Using try_files mean that you can test a sequence. If $uri doesn't exist, try $uri/, if that
doesn't exist try a fallback location. In this case it will see if the $uri file exists. If it does then serve
it. If it doesn't then tests if that directory exists. If not, then it will proceed to serve index.html which
you make sure exists. It's loaded but oh so simple. This is another instance you can completely
eliminate If.

3. The following code is also considered as BAD practice.


server {
server_name domain.com *.domain.com;
if ($host ~* ^www\.(.+)) {
set $raw_domain $1;
rewrite ^/(.*)$ $raw_domain/$1 permanent;
[...]

}
}
With if directives Nginx is forced to evaluate every request for all domains. Evaluating every
request against if directives is extremely inefficient. Avoid using if directives and use two server
directives as shown below :
server {
server_name www.domain.com;
return 301 $scheme://domain.com$request_uri;
}
server {
server_name domain.com;
[...]
}

5. Apache rules to Nginx rules conversion


There is a long history of writing rewrite rules for Apache's powerful mod_rewrite
module, and most resources on the Internet are focused on these. When encountering
rewrite rules in Apache's format, they can be translated into a form that NGINX can
parse by following a few simple rules.
Rule #1: Replace directory and file existence checks with try_files
When encountering an Apache rewrite rule of the following form:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L]
This can best be translated into an NGINX configuration as follows:
try_files $uri $uri/ /index.php?q=$uri;

Rule #2: Replace matches against REQUEST_URI with a location


When encountering an Apache rewrite rule of the following form:
RewriteCond %{REQUEST_URI} ^/niceurl

RewriteRule ^(.*)$ /index.php?q=$1 [L]


This is best handled in NGINX by using a location:
location /niceurl {
include fastcgi_params;
fastcgi_index index.php;
fastcgi_pass 127.0.0.1:9000;
}
This principle also applies to RewriteRules that have an implicit REQUEST_URI. These are
typically bare RewriteRules that transform the URI from an older format to a newer one. In the
following example, we see that the show.do is no longer necessary:
RewriteRule ^/controller/show.do$ http://example.com/controller
[L,R=301]
This translates to an NGINX configuration as follows:
location = /controller/show.do {
rewrite ^ http://example.com/controller permanent;
}
Not to get too carried away with creating locations whenever we see a RewriteRule, we should keep
in mind that regular expressions translate directly.
Rule #3: Replace matches against HTTP_HOST with a server
Related closely to Rule #2, this rule takes configurations into account that try to either remove or
add a www onto a domain name. These types of rewrite rules are often found in .htaccess files or in
virtual hosts with overloaded ServerAliases:
RewriteCond %{HTTP_HOST} !^www
RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301]
Here, we translate the case where no www is found at the beginning of the Host part of the URL to
the variant with a www there:
server {

server_name example.com;
rewrite ^ http://www.example.com$request_uri permanent;
}
Rule #4: Replace RewriteCond with if for variable checks
This rule applies only after having applied rules 1 to 3. If there are any remaining conditions not
covered by those rules, then if may be applied to test the values of variables. Any HTTP variable
may be used by prefixing the lowercased name of the variable with $http_. If there are hyphens (-)
in the name, these are translated into underscores (_).
The following example (taken from Apache's documentation on the mod_rewrite module at
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html) is used to decide which page should be
delivered to a client based on the User-Agent header:
RewriteCond %{HTTP_USER_AGENT} ^Mozilla
RewriteRule ^/$ /homepage.max.html
RewriteCond %{HTTP_USER_AGENT} ^Lynx
RewriteRule ^/$ /homepage.min.html [L]
RewriteRule ^/$ /homepage.std.html [L]
This can be translated to an NGINX configuration as follows:
if ($http_user_agent ~* ^Mozilla) {
rewrite ^/$ /homepage.max.html break;
}
if ($http_user_agent ~* ^Lynx) {
rewrite ^/$ /homepage.min.html break;
}
index homepage.std.html;
6. Apache rules to Nginx rules conversion tool

The apache configurations can be converted to the nginx configuration using this tool :
http://winginx.com/htaccess
http://labs.gidix.de/nginx/
7. Dependencies
1. server name in redirect
Syntax: server_name_in_redirect on | off;
Default: off
Context: http, server, location
Enables or disables the use of the primary server name, specified by the server name
directive, in redirects issued by nginx. When disabled, the name from the Host request header field
is used. If this field is not present, an IP address of the server is used.
2. port in redirect
Syntax: port_in_redirect on | off;
Default: on
Context: http, server, location
Enables or disables specifying the port in redirects issued by nginx.
8. References
1. http://wiki.nginx.org/NginxHttpRewriteModule
2. http://wiki.nginx.org/Pitfalls
3. http://wiki.nginx.org/IfIsEvil
4. http://agentzh.blogspot.in/2011/03/how-nginx-location-if-works.html
5.
Note:

You might also like