A 301 redirect in .htaccess tells a browser and every search engine crawler that a URL has moved permanently, and to send both traffic and ranking credit to the new address instead. Get the syntax slightly wrong, a missing slash, the wrong flag, a rule placed in the wrong order, and you end up with a redirect loop, a 404 masquerading as a working link, or a redirect that quietly drops the query string a tracking link depended on. These are the recipes that work, copy-paste ready, plus the mistakes that account for most broken .htaccess redirects in practice.

Why 301 specifically, and what it does for SEO
The reason to reach for a 301 rather than any other redirect is what it signals to search engines. Google has confirmed that a correctly implemented 301 passes essentially 100% of PageRank (link equity) from the old URL to the new one, Gary Illyes stated back in 2016 that 30x redirects no longer lose PageRank, and that has held ever since. In practice that means a well-executed 301 migration preserves the ranking you spent years earning, while a botched one throws it away.
Two caveats that still cost real equity in 2026, both worth designing around:
- Redirect chains leak. Each extra hop (A → B → C) adds latency and risk, and long chains can dilute or drop signals. Redirect old URLs directly to their final destination, not through a stack of intermediate redirects left over from previous migrations.
- Irrelevant targets get treated as soft 404s. If you redirect a retired page to something unrelated (the classic "just send everything to the homepage"), Google may classify it as a soft 404 and pass no equity at all. Map each old URL to its closest real equivalent, and only fall back to a parent category or the homepage when nothing better exists.
The single most common recipe: one URL to another
Redirect 301 /old-page/ /new-page/
This is mod_alias syntax, the simplest form, and it's the right tool for a single, permanent, one-to-one move. It works on virtually every Apache host without extra configuration. The path on the left is relative to the site root, the path on the right can be relative or a full URL if you're redirecting to a different domain entirely.
For redirecting to a different domain:
Redirect 301 /old-page/ https://example.com/new-page/
Redirecting an entire folder
RedirectMatch 301 ^/old-folder/(.*)$ /new-folder/$1
This uses RedirectMatch, which supports regular expressions, so every URL under /old-folder/ carries its trailing path over to the equivalent URL under /new-folder/. /old-folder/page-a becomes /new-folder/page-a, /old-folder/sub/page-b becomes /new-folder/sub/page-b, without writing a separate rule for every page inside it. This is the recipe to reach for during a site restructure where a whole section is moving, not just one page.
Forcing HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
This switches to mod_rewrite syntax, which is the right tool once a redirect needs a condition rather than a flat one-to-one mapping. RewriteCond %{HTTPS} off only fires the rule when the request wasn't already secure, so it won't create a loop on requests that are already HTTPS. [L,R=301] means "last rule, apply as a 301," L stops Apache from evaluating further rewrite rules on this request once it matches.
Forcing (or removing) the www subdomain
Force www:
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Remove www:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1%{REQUEST_URI} [L,R=301]
Pick one direction and stay consistent everywhere, in internal links, canonical tags, and anywhere the domain is hardcoded, a site that redirects inconsistently between www and non-www across different pages is a common, self-inflicted source of duplicate-content confusion for crawlers.
Redirecting an old file extension (e.g. a CMS migration)
RewriteEngine On
RewriteRule ^(.*)\.html$ /$1 [R=301,L]
Common after moving off a static-HTML site or an old CMS onto something that serves clean URLs without an extension. $1 captures everything before .html and the rule reissues it without the extension attached.
Bulk redirects, mapped in a table
For a large batch of one-to-one redirects, typically the output of a site migration, mod_alias's Redirect 301 lines stacked one per row are more maintainable than one giant regex:
| Old URL | New URL | Rule |
|---|---|---|
| /blog/old-post-name/ | /blog/new-post-name/ | Redirect 301 /blog/old-post-name/ /blog/new-post-name/ |
| /services | /what-we-do | Redirect 301 /services /what-we-do |
| /product-a/ | /products/product-a/ | Redirect 301 /product-a/ /products/product-a/ |
A migration with more than roughly 30-40 individual mappings gets unwieldy as flat .htaccess lines, at that scale a database-driven redirect map or a plugin (on a CMS) that stores the table outside the server config is worth the extra setup, since .htaccess is re-parsed on every request and a very long file adds real overhead.
The mistakes that actually break redirects
- Rule order. Apache processes
.htaccessrules top to bottom and stops at the first match whenLis set. A broadRewriteRuleplaced above a specific one will catch the request first and the specific rule never fires. Put specific, narrow rules before broad, catch-all ones. - Redirect loops. The most common cause is a
RewriteCondthat doesn't actually exclude the already-redirected state, an HTTPS-forcing rule with noRewriteCond %{HTTPS} offcheck will redirect a request that's already secure right back to itself. - Missing or extra trailing slashes.
/old-pageand/old-page/are technically different URLs to Apache. A rule written for one won't reliably match a request for the other unless you account for both, which is a frequent source of a redirect that "works" in a browser (which often normalizes the slash) but fails for a crawler or a script hitting the exact URL. - 302 instead of 301.
[R=301]and a bare[R](which defaults to a 302, temporary) are not interchangeable. A permanent move issued as a 302 tells search engines the change might not stick, so ranking signals aren't reliably passed to the new URL, exactly the outcome a permanent redirect is meant to avoid. - Losing the query string. By default,
mod_rewriteappends the original query string to the destination unless you add theQSA(query string append) flag or explicitly suppress it with?at the end of the target. If a redirect is dropping UTM parameters or other tracking data a link depended on, this flag is usually the fix:[R=301,L,QSA]. - Case sensitivity. Apache's default matching is case-sensitive.
/Old-Page/and/old-page/are different requests unless a rule explicitly handles both, worth checking if a redirect works for a lowercase link but a stakeholder reports it broken on a link they typed with different capitalization.
Testing before you ship it
Once a rule is live, don't assume it works from a single click in your own browser, browsers cache redirects aggressively, so a rule you already tested once can appear to work even after you've changed or broken it. Test in a private/incognito window, or with a command-line request that shows the actual status code:
curl -I https://example.com/old-page/
The response should show HTTP/1.1 301 Moved Permanently and a Location: header pointing at the correct destination. If it shows 302, the flag is wrong. If it shows 200 on the old URL, the rule isn't matching at all, most often a rule-order or trailing-slash issue from the list above.
For a batch of redirects after a migration, don't test them by hand one at a time either, loop the whole old-URL list through curl -I in a short script and grep for any response that isn't a 301. A single 200 or 404 in that output is a rule that didn't match, and finding it before launch is the difference between a clean migration and a week of slow-bleeding traffic loss nobody notices until the next analytics review.
This is exactly the kind of thing worth confirming on the actual checklist, not from memory, before a migration goes live, we cover the fuller pre-launch pass, including checking every changed link resolves correctly, in the website QA checklist. Redirect verification is one line item in the broader launch pass laid out in what is QA testing, which frames why catching a broken redirect pre-launch is so much cheaper than catching it after.
Building the rule without hand-writing regex
Getting mod_rewrite regex exactly right by hand, especially the capture groups and flag combinations above, is the part that eats the most time on a migration with more than a handful of URLs. Our free .htaccess redirect generator takes the old and new URL plus the options that matter (301 vs 302, www handling, HTTPS forcing) and outputs the correct block to paste in, so the syntax details in this post don't have to be memorized every time you touch a redirect.
Where Shotline fits
A broken redirect is a specific, checkable kind of bug, wrong status code, wrong destination, a loop, and it's exactly the kind of thing that's easy to miss in a normal click-through and obvious the moment a QA pass actually checks status codes on every changed URL. Shotline lets anyone reviewing a live site pin feedback directly to the page in question, with the URL, viewport, and console output attached, so a flagged redirect issue reaches a developer or coding agent with everything needed to fix it, not just "this link goes to the wrong place."
Shotline is free to try for 14 days, no card required, then from $19/mo (billed annually; $25 month-to-month). See how it fits into a launch checklist on the Shotline homepage, or read the fuller workflow in what is vibe coding.




