dominik.im|Rewrite Rules Generator

DE

Online Marketing

Rewrite Rules Generator

Paste old and new URLs and generate clean redirect rules for Apache Mod_Rewrite or NGINX rewrite rules.

URL pairs
Status code

Help with rewrite rules for Apache and NGINX

Rewrite rules and redirects help route old URLs cleanly to new targets, switch HTTP to HTTPS, prevent duplicate content and handle relaunches without unnecessary ranking loss. The generator creates suitable individual redirects for Apache Mod_Rewrite and NGINX. The following examples show typical cases that are often additionally needed in server configurations.

Enable the basics

Apache commonly uses Mod_Rewrite in an .htaccess file. The Rewrite Engine has to be enabled once; the generator intentionally does not include this activation line with every individual redirect. NGINX does not use .htaccess; rules usually live in the matching server block.

Apache Mod_Rewrite
RewriteEngine On
NGINX
server {
  listen 80;
  server_name example.com www.example.com;
}

Comments start with # on both servers. A commented-out rule is not executed.

# RewriteRule .* /index.php [L]

Redirect HTTP to HTTPS

A permanent HTTPS redirect ensures that visitors and search engines only use the encrypted version. The status code 301 signals a permanent change. In Apache, R=301 or R=302 creates an external redirect; without R, an internal rewrite takes place. L stops the current rule chain as soon as the rule matches.

Apache Mod_Rewrite
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
NGINX
server {
  listen 80;
  server_name example.com www.example.com;
  return 301 https://$host$request_uri;
}

Alternatively, Apache can check the SSL port. This is helpful when %{HTTPS} is not reliably set in a hosting environment.

RewriteCond %{SERVER_PORT} !443$
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

Unify the www variant

A fixed host variant prevents duplicate content. The example redirects from example.com to www.example.com while keeping path and query string. ^ marks the beginning and $ the end of the string. NC makes the comparison case-insensitive.

Apache Mod_Rewrite
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
NGINX
server {
  server_name example.com;
  return 301 https://www.example.com$request_uri;
}

Add trailing slashes

This rule adds a trailing slash to directory URLs. Existing files are excluded so CSS, JavaScript, images and downloads are not redirected.

Apache Mod_Rewrite
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ %{REQUEST_SCHEME}://%{HTTP_HOST}/$1/ [R=301,L,NE]
NGINX
location ~ ^(.+[^/])$ {
  if (!-f $request_filename) {
    return 301 $scheme://$host$1/$is_args$args;
  }
}

The Apache flag NE means no escape. In older Apache environments, it prevents unwanted escaping for redirects with query strings.

Change domain with identical paths

During a domain change, the requested path is kept. In Apache, the condition also matches www.old-example.com and subdomains because the host is only checked at the end.

Apache Mod_Rewrite
RewriteCond %{HTTP_HOST} old\-example\.com$ [NC]
RewriteRule ^(.*)$ https://www.new-example.com/$1 [R=301,L]
NGINX
server {
  server_name old-example.com www.old-example.com;
  return 301 https://www.new-example.com$request_uri;
}

Redirect a single URL

Relaunches often map old detail pages to new target pages. This is exactly what the generator is designed for because old and new URLs can be entered line by line. Apache can check the URL directly in the RewriteRule or via %{REQUEST_URI} in a condition.

Apache Mod_Rewrite
RewriteRule ^old-page.php$ https://www.example.com/new-page/ [R=301,L]
RewriteCond %{REQUEST_URI} old-page\.php$
RewriteRule ^(.*)$ https://www.example.com/new-page/ [R=301,L]
NGINX
location = /old-page.php {
  return 301 https://www.example.com/new-page/;
}

Redirect a folder in the URL

If only a directory name changes, the remaining path can be reused via a capture group. $1 contains the first parenthesized match in both Apache and NGINX.

Apache Mod_Rewrite
RewriteRule ^directory/(.*)$ /other-directory/$1 [R=301,L]
NGINX
rewrite ^/directory/(.*)$ /other-directory/$1 permanent;

Redirect URLs with query strings

GET parameters belong in a RewriteCond in Apache. If only one specific parameter is relevant, (^|&)site=index(&|$) checks this parameter regardless of its position in the query string. The question mark at the end of the Apache target removes the old query string. In NGINX, an exact location block with $arg_site is usually more robust for simple query redirects than comparing the full $request_uri.

Apache Mod_Rewrite
RewriteCond %{QUERY_STRING} (^|&)site=index(&|$)
RewriteRule ^index\.php$ https://www.example.com/home/? [R=301,L]

If multiple query parameters are present in the source and all of them must match exactly, the complete query string can be checked instead. In that case, the order of the parameters matters.

RewriteCond %{QUERY_STRING} ^site=index&ref=google$
RewriteRule ^index\.php$ https://www.example.com/home/? [R=301,L]
NGINX
location = /index.php {
    if ($arg_site = "index") {
  return 301 https://www.example.com/home/;
    }
}

If the complete URI including the order of all query parameters must match exactly, $request_uri can deliberately be used. This form should not be nested inside another if.

if ($request_uri = "/index.php?site=index&ref=google") {
    return 301 https://www.example.com/home/;
}

For many redirects or combined conditions, map belongs in the http block. The redirect itself remains a simple if with return in the matching server block.

map "$host|$uri|$arg_site" $redirect_target {
    default "";
    "example.com|/index.php|shoes" https://www.example.com/shoes/;
    "example.com|/index.php|pants" https://www.example.com/pants/;
}

server {
    server_name example.com;

    if ($redirect_target) {
  return 301 $redirect_target;
    }
}

Serve login via HTTPS only

The example separates login pages from other pages. In production projects, a complete HTTPS redirect is usually preferable.

Apache Mod_Rewrite
RewriteCond %{HTTPS} on
RewriteCond %{REQUEST_URI} !login/
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1 [R=301,L]

RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} login/
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
NGINX
location /login/ {
  return 301 https://$host$request_uri;
}

Temporarily enable a maintenance page

Apache only redirects to the maintenance page if maintenance.html exists. 307 stands for a temporary redirect. The maintenance page itself should additionally send the status code 503 Service Temporarily Unavailable. In NGINX, the maintenance file can be used directly as the error page for 503.

Apache Mod_Rewrite
RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ %{REQUEST_SCHEME}://%{HTTP_HOST}/maintenance.html [R=307,L]
NGINX
error_page 503 /maintenance.html;

if (-f $document_root/maintenance.html) {
  return 503;
}

location = /maintenance.html {
  internal;
  add_header Retry-After 3600 always;
}

Block IP access

The Apache flag F returns 403 Forbidden. Multiple RewriteCond lines are AND-linked by default in Apache. With [OR], the current condition is OR-linked with the following condition. The IPs in the example come from the reserved documentation network 203.0.113.0/24.

Apache Mod_Rewrite
RewriteCond %{REMOTE_ADDR} ^203\.0\.113\.10$ [OR]
RewriteCond %{REMOTE_ADDR} ^203\.0\.113\.11$
RewriteRule .* - [F]
NGINX
deny 203.0.113.10;
deny 203.0.113.11;
allow all;

Block referrer access

Referrer spam can be blocked server-side. Apache uses %{HTTP_REFERER} for this, while NGINX uses the variable $http_referer. NC and ~* check without considering upper and lower case.

Apache Mod_Rewrite
RewriteCond %{HTTP_REFERER} example\.com [NC]
RewriteRule .* - [F]
NGINX
if ($http_referer ~* example\.com) {
  return 403;
}

Front controller for applications

Many PHP applications internally route non-existing files, folders and symbolic links to index.php. This is not a redirect, but internal routing. NGINX usually uses return for simple redirects, while internal routing often uses try_files or rewrite ... last.

Apache Mod_Rewrite
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule .* index.php [L]
NGINX
location / {
  try_files $uri $uri/ /index.php?$query_string;
}

Route file extensions internally

These examples rewrite requests internally. The address in the browser stays unchanged. This is different from an external redirect with 301 or 302.

Apache Mod_Rewrite
RewriteRule ^(.*)\.html$ $1.php [L]
RewriteRule ^site_([0-9]+)\.html$ site.php?id=$1 [L]
NGINX
rewrite ^/(.*)\.html$ /$1.php last;
rewrite ^/site_([0-9]+)\.html$ /site.php?id=$1 last;

Rewrite internally based on cookie value

Cookies can be used to control variants or test versions server-side. The first example is suitable for a simple A/B test and routes internally to variant-default.php if the cookie abtest=variant-b is not present. Access control should not rely only on such rewrite rules.

Apache Mod_Rewrite
RewriteCond %{HTTP_COOKIE} !(^|;\s*)abtest=variant-b(;|$) [NC]
RewriteRule .* variant-default.php [L]
NGINX
if ($http_cookie !~* "(^|;\s*)abtest=variant-b(;|$)") {
  rewrite ^ /variant-default.php last;
}

The second example checks a cookie value with an allowed digit length. [0-9]{2,4} means two to four digits. This can be used to route numbered campaign variants internally.

Apache Mod_Rewrite
RewriteCond %{HTTP_COOKIE} (^|;\s*)campaign_id=[0-9]{2,4}(;|$) [NC]
RewriteRule ^offer/?$ offer-campaign.php [L]
NGINX
if ($http_cookie ~* "(^|;\s*)campaign_id=[0-9]{2,4}(;|$)") {
  rewrite ^/offer/?$ /offer-campaign.php last;
}

Serve WebP versions automatically

This example describes content negotiation for images. If the browser supports WebP and a matching file such as image.jpg.webp exists next to the original, the WebP file is served internally. Vary: Accept is important so caches can distinguish between browsers with and without WebP support.

Apache Mod_Rewrite
AddType image/webp .webp
SetEnvIf Request_URI "\.(jpe?g|jpe|png|gif)$" vary_accept=1
Header append Vary Accept env=vary_accept

RewriteCond %{HTTP_ACCEPT} image/webp [NC]
RewriteCond %{REQUEST_FILENAME}.webp -f
RewriteRule ^(.+)\.(jpe?g|jpe|png|gif)$ $1.$2.webp [T=image/webp,L]
NGINX
location ~* ^(/.+)\.(jpg|jpeg|jpe|png|gif)$ {
  add_header Vary Accept always;

  if ($http_accept ~* "image/webp") {
    set $imwebp A;
  }
  if (-f $request_filename.webp) {
    set $imwebp "${imwebp}B";
  }
  if ($imwebp = AB) {
    rewrite ^(.*)$ $1.webp last;
  }
}