Developer
Redirect & Rewrite Rule Generator
Generate 301/302 redirects and rewrite rules for Apache .htaccess (mod_rewrite), Nginx and more. Supports regex patterns and bulk lists. Copy-ready output.
One old/new pair per line. Start a regular expression with ^ and end it with $. A * is one wildcard.
Add one old and new path per line. Use a comma, spaces, or ->. Status defaults to 301.
Redirect rules
—
Output actions
Ready to copy output once your result appears.
Redirect & Rewrite Rule Generator mini guide
This rewrite rule generator turns an old URL path and a new URL path into a redirect or an internal rewrite. Paste one pair per line and copy Apache Redirect, Apache RewriteRule, Nginx return, Nginx rewrite, a bulk list, Netlify _redirects, or a Next.js redirects() function. Generation runs in your browser. The list is not uploaded.
How to use
- 1Enter one old path and one new path per line. Separate them with a comma, spaces, or ->, and add 301, 302, 307, or 308 when you do not want the default 301. A line such as /old-page, /new-page, 301 is enough.
- 2Choose the output mode that matches the server. Apache Redirect writes a mod_alias directive. Apache RewriteRule writes RewriteEngine On and a rule with an [R=301,L] flag. Nginx offers return and rewrite. Bulk mode prints one pair per line. Netlify writes _redirects. Next.js writes redirects().
- 3Generate the rules, copy them into .htaccess, the Nginx server block, the Netlify redirects file, or next.config.js, then request the old path and confirm the status code before you ship the file.
What it is good for
- • Moving a renamed page or a whole folder without leaving the old URL broken.
- • Preparing a migration list when each old path has one new path.
- • Comparing Apache, Nginx, Netlify, and Next.js syntax for the same pair.
Example
Input
/old-page, /new-page, 301
Output
RewriteEngine On RewriteRule ^old-page$ /new-page [R=301,L]
Apache Redirect mode prints Redirect 301 /old-page /new-page for the same row. The pattern has no leading slash because .htaccess matches the path relative to the directory.
Redirect vs rewrite
A redirect is an external response. The server answers the old URL with a status such as 301 or 302 and a Location header that names the new URL. The browser, the crawler, and the HTTP client then make a second request for that new URL. The address bar changes. Analytics, caches, and search engines can record the old URL as a pointer to the new one. Use a redirect when a page has moved and you want every client, including Google, to learn the new address.
A rewrite is an internal mapping. The server chooses a different file or upstream path and still responds to the URL the client asked for. The address bar stays on the original path. Users and crawlers do not see a second hop unless the rule also sets a redirect flag. Apache does this with RewriteRule and no R flag, or with a proxy flag. Nginx does it with rewrite ... last or with a location that passes the request upstream. An internal rewrite is the right tool when you want a pretty URL to serve an existing file without advertising a different public address.
The modes on this page that include R=301, return 301, or permanent are external redirects, even when the directive is named rewrite. Apache's RewriteRule becomes a redirect only because of the R flag. Nginx's rewrite ... permanent sends a 301. Nginx's return 301 is a redirect with no extra rewrite engine. If you need a silent internal map, remove the redirect flag in your own config and test it. This generator keeps the flag on, because the search queries that reach this page are about publishing a new URL, not about hiding one.
External and internal rules also differ in where they run. A redirect can point at another host, which is how an HTTP site sends people to HTTPS or how a www host sends people to the apex domain. An internal rewrite usually stays on the same server. Do not point an internal rewrite at a URL on another origin and expect the content to appear under the old host. That needs a proxy, and a proxy is a different design with its own caching and header rules.
301 vs 302 vs 308
301 means the move is permanent. Browsers and intermediate caches may store the mapping. Search engines generally transfer signals from the old URL to the new URL and drop the old URL from the index over time. Use 301 when a slug, a folder, or a host has a lasting replacement. 302 means the move is temporary. Caches treat it as short-lived, and search engines usually keep the original URL as the indexed address. Use 302 for a short campaign, a holding page, or a test you plan to undo.
307 and 308 keep the HTTP method and the body. A 301 or 302 is allowed to turn a POST into a GET on the next request. A 307 is the temporary method-preserving status. A 308 is the permanent method-preserving status. Use 307 or 308 when a form, an API client, or a webhook posts to the old path and must post again to the new path. For ordinary page moves that are only followed with GET, 301 and 302 remain the statuses people expect to see in server logs.
Next.js redirects() does not offer a 301 or 302 switch. permanent: true responds with 308. permanent: false responds with 307. This page maps 301 and 308 to permanent: true, and 302 and 307 to permanent: false, and the generated function says so in a comment. Google treats 308 as a permanent redirect, so a page move expressed as 308 is a normal choice on Vercel and Next.js. If a partner or an older client must see the literal number 301, emit the Apache or Nginx rule instead of the Next.js function.
Pick the status per line. A migration file can mix a permanent folder move with a temporary promo URL. The generator does not upgrade a 302 to a 301. Leaving the status off selects 301, because a forgotten code should not publish a temporary redirect that search engines keep revisiting. If you are unsure, ship 302 first, confirm the destination, then change the line to 301 once the new URL is the one you want cached.
Common recipes
HTTP to HTTPS is a host rule, not a pair of paths. The generator asks for paths because each row is one public URL path. The host redirect belongs in the server block that still listens on port 80. In Apache, test the scheme with a condition and then redirect the original host and URI. In Nginx, a small server block that only returns 301 is easier to read than a rewrite. Keep this redirect in one place so application routes do not each repeat it.
WWW to apex is the same shape. Match the host www.example.com and send the client to https://example.com with the original path and query attached. Do the reverse if the canonical host is www. One hop is enough: HTTP www should land on HTTPS apex without an intermediate HTTP apex response. HSTS belongs on the HTTPS response after you have watched the redirect work. A strict transport header on a broken redirect is painful to undo.
Trailing-slash policy should be one rule for the whole site. To remove a trailing slash, match a path that ends with / and redirect to the same path without it, and exempt real directories if Apache is serving files from disk. To add a trailing slash, match a path that does not end with / and is not a file. Pick one style, list it in the sitemap, and stop mixing both. A slash redirect and a folder redirect that fire on the same request can loop. The L flag and Nginx's return stop the current rule set, but a second server-level redirect can still chain.
A folder move is a capture group. The source ^/old-folder/(.*)$ and the destination /new-folder/$1 keep the rest of the path. In .htaccess the same pattern is written without the leading slash, as ^old-folder/(.*)$, because Apache removes the directory prefix before RewriteRule runs. Nginx keeps the leading slash. Netlify writes /old-folder/* and /new-folder/:splat. Next.js writes /old-folder/:path* and /new-folder/:path*. Named pieces such as /blog/:year/:slug stay named on Netlify and Next.js, and become ([^/]+) groups with $1 and $2 on Apache and Nginx.
A literal dot or plus sign is not a wildcard. The path /files/a.pdf is an exact path. In a RewriteRule the dot is escaped as \. so it does not mean "any character". A question mark is rejected in the source, because the query string is not part of the path match. A star in /blog/* is a wildcard: Apache and Nginx receive (.*), Netlify keeps the star as a splat, and Next.js receives :path* at the end of the path. To force a regular expression, start the source with ^ and end it with $, as in ^/blog/(.*)$. That is the form used for the capture-group example above.
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}How each output mode is formed
Apache Redirect 301 uses mod_alias. A literal row becomes Redirect 301 /old-page /new-page. The status is the number you entered. A wildcard or a ^ pattern becomes RedirectMatch, because plain Redirect does not capture groups. Redirect is prefix-based for literals, so /old also matches /old/extra. When that prefix behavior is too wide, use the RewriteRule mode, which anchors the pattern with ^ and $.
Apache RewriteRule mode prints RewriteEngine On once, then one RewriteRule per row. The flag is [R=301,L] for a permanent redirect, or [R=302,L], [R=307,L], or [R=308,L] for the other statuses. L means this is the last rewrite rule. The pattern omits the leading slash so it fits a per-directory .htaccess file. A percent sign or dollar sign in a literal destination is escaped so Apache does not treat it as a backreference. Destinations that already contain $1 are left as backreferences.
Nginx return mode prints a location block. An exact path uses location = /old-page and return 301 /new-page. A pattern uses location ~ and the regular expression, and the destination may contain $1. Nginx rewrite mode prints rewrite ^/old-page$ /new-page permanent for 301 and rewrite ... redirect for 302. Nginx has no rewrite flag for 307 or 308, so those rows are emitted as return inside a location even when rewrite mode is selected. The status in the file is the status you asked for.
Bulk mode prints one old path, one new path, and the status on each line. It does not add server syntax. Use it to review a migration spreadsheet before you generate Apache or Nginx, or to hand the list to someone who will write the rules in another system. Blank lines and lines that start with # are ignored in every mode. Netlify _redirects uses the same three columns. A capture becomes a splat: /blog/* /news/:splat 301. Place that file at the site root, or use the redirects key in netlify.toml if that is how the project is already organized.
Next.js mode prints an async redirects() function you can paste into next.config.js. Each object has source, destination, and permanent. The comment at the top records the 308 and 307 behavior. A trailing * becomes :path*. A source such as /blog/:slug is copied through, because that is already Next.js path syntax. Vercel uses this same function. Paths are JSON-escaped. The function does not merge with redirects you already have. Open the existing array and add the new objects if the project already returns some.
Testing rules
A rule that looks right in the text area can still lose to another rule on the server. Apache applies Redirect and RewriteRule in a defined order, and a rule earlier in the file with the L flag, or a more specific location in Nginx, can hide the line you just added. After you deploy, request the old URL with curl -I so you see the status and the Location header without following the chain. Then request it in a browser with the cache disabled. A cached 301 from a previous test will keep sending you to the old target even after you edit the file.
Check the path you think you are matching. In .htaccess, RewriteRule sees the path with the directory prefix removed and without the query string. A pattern copied from Nginx, where the leading slash is present, will not match in .htaccess until that slash is removed. This generator removes it for the RewriteRule mode and keeps it for Nginx. Query strings are not part of the match. If the old URL is /old-page?utm=spring, the source is /old-page. Put a replacement query on the destination only when you want to discard the original query.
Watch for loops. /new to /old and /old to /new, or a trailing-slash rule that fights a folder rule, will bounce until the browser stops. The L flag does not protect you from a second redirect on the destination. Request the destination too, and confirm it returns 200. For a long list, test the first row, the last row, a row with a dot in the path, and a row with a capture. Those four cases catch escaping mistakes and off-by-one group numbers.
On Apache, apachectl configtest or httpd -t checks that the file parses. On Nginx, nginx -t does the same. A parsed file can still contain a rule that never matches. The access log shows the status you returned. RewriteLog or LogLevel rewrite:trace3 on Apache, used briefly on a staging host, shows which pattern ran. Turn that tracing off afterward. Netlify and Vercel show redirect results in their deploy logs and in the response headers. None of those checks happen inside this browser tool. The tool only renders text. Split a suspicious path with the URL parser before you encode it, list the destination in the XML sitemap, and keep robots.txt from disallowing the page you just redirected to. The meta tag generator is a separate step for the title and description on the new URL.
Rules and limitations
Each line is one pair. Commas, spaces, and -> are accepted. The status may be 301, 302, 307, or 308, and it defaults to 301. Lines that are blank or start with # are skipped. More than 1,000 rows is rejected. A source must be a path, not a full URL, and it must not contain a query string or a raw space.
A source that starts with ^ is a regular expression and must end with $. Other sources are literal paths, except that * is one wildcard and :name is one path segment. Literal regular-expression characters such as a dot are escaped in RewriteRule and Nginx rewrite patterns. Apache Redirect mode uses Redirect for literals and RedirectMatch when there is a capture.
Netlify and Next.js output supports one capture. A pattern with two groups, or a wildcard that is not at the end of a Next.js path, asks you to use Apache or Nginx instead of emitting a rule that those platforms would misread. Percent signs in a literal Apache substitution are escaped so they are not read as rewrite backreferences.
Common problems
RewriteRule did nothing: the file may be ignored because AllowOverride does not permit FileInfo, RewriteEngine was left Off, or the pattern still has the leading slash that .htaccess already stripped. A rule in a <Directory> or server config sees the leading slash, while a rule in .htaccess does not. The RewriteRule mode targets .htaccess.
The redirect loops or the wrong page wins: look for an earlier rule that already matches, including a prefix Redirect /old that also matches /old-page. Anchored RewriteRule patterns avoid that prefix trap. Also check that the destination does not redirect back, and that a CDN has not stored an older 301.
A captured path is empty or includes too much: (.*) crosses slashes, and ([^/]+) does not. $1 refers to the first group, not the first directory. Count opening parentheses from the left, and skip groups that begin with (?: if you add those by hand. This generator's :slug tokens become ([^/]+) in that left-to-right order.
Frequently asked questions
Where do I put .htaccess?
Put the file in the directory that should apply the rules, often the site root, and name it exactly .htaccess. Apache only reads it when AllowOverride permits FileInfo for that directory. Restarting is usually unnecessary for .htaccess changes, but the server must have mod_alias for Redirect and mod_rewrite for RewriteRule. Nginx does not read .htaccess. Paste Nginx output into the server or location block and reload Nginx. Netlify reads _redirects from the publish directory. Next.js reads redirects() from next.config.js.
Why isn't my RewriteRule working?
The usual causes are a missing RewriteEngine On, a pattern that still starts with a slash in .htaccess, a rule earlier in the file that already matched, or AllowOverride blocking the file. The pattern is a regular expression, so an unescaped dot matches any character. This generator turns RewriteEngine On on once, strips one leading slash for .htaccess, anchors literal paths, and escapes regex characters in those literals. If you wrote your own ^ pattern, the generator keeps your groups and only adjusts the leading slash.
How does regex escaping work in a rewrite rule?
In a RewriteRule or an Nginx rewrite, the source is a regular expression. A dot matches any character, a plus sign repeats the character before it, and a question mark makes it optional. A literal file name such as report.pdf has to be written as report\.pdf or the dot matches reportXpdf too. This page escapes those characters when you enter a normal path. When you enter a pattern that starts with ^, it does not escape the pattern, because the parentheses and the dot-star are the capture you asked for. Dollars in a literal Apache destination are escaped so $ is not read as a group. $1 in a destination you wrote yourself is kept.
Can I generate a bulk list of old and new URLs?
Yes. Bulk mode prints one old path, one new path, and the status code on each line, with no server syntax wrapped around them. The other modes walk the same list and print one rule per line, or one location block per line for Nginx return. Paste a spreadsheet export as comma-separated rows or as path path status. Up to 1,000 rows are accepted. The result is text you copy. It is not saved in the browser and it is not sent to a server by this page.
Is my input uploaded or saved?
This tool processes input in your browser and does not save its input or output to local storage. Copy and download are user actions. The site uses analytics for page and action metadata and may use ads and production error reporting; see the Privacy Policy for those services.
About this tool
Maintained by Toolbench. For large or deeply nested files, split the input into smaller batches and verify representative results before using them in an import or application. Report reproducible problems through Contact.
Next steps
Related tools
Continue the workflow with nearby tools for formatting, publishing, debugging, or follow-up checks.
URL and Query String Parser
Split a URL or query string into components and decoded parameters.
Open URL and Query String ParserMeta Tag Generator
Generate title, meta description, canonical, Open Graph, and Twitter tags.
Open Meta Tag GeneratorRobots.txt Generator
Create a robots.txt file with allow, disallow, and sitemap rules.
Open Robots.txt GeneratorXML Sitemap Generator
Turn absolute URL rows into a sitemap urlset.
Open XML Sitemap GeneratorTrust
Policies, contact, and answers
Clear pages for how Toolbench works, what we collect, and how to reach us.