Skip to main content

.htaccess Redirect Recipes: WWW, HTTPS, and Trailing Slashes

9 months ago
0
0
0

You want one clean URL for every page. No chains. No loops. Just fast, canonical URLs that help SEO and users. These Apache/LiteSpeed .htaccess recipes are safe defaults you can paste today. They also play nicely with WordPress.

Heads-up: .htaccess works on Apache/LiteSpeed (including Pofii-Tuned LiteSpeed). For NGINX, use server blocks instead.


Why redirects matter (30 seconds)

  • Consistency: one canonical URL (https + www or not + slash policy).
  • Speed: avoid redirect chains (HTTP → HTTPS → non-www).
  • SEO: link equity and metrics live on the canonical.
  • Logs: clean analytics and easier debugging.

How to test (do this before and after)

  • In a terminal:
curl -I http://example.com/page
curl -I https://www.example.com/page/
curl -I -L http://example.com/page?ref=test
  • You should see one 301 to your final URL.
  • Query strings should remain intact.

Base setup (put this at the top)

# Enable rewriting
RewriteEngine On

# Respect proxies (Cloudflare/ELB) to avoid loops
RewriteCond %{HTTP:X-Forwarded-Proto} =https [OR]
RewriteCond %{HTTPS} =on
RewriteRule ^ - [E=HTTPS_ON:1]

This marks requests as HTTPS when a proxy terminates TLS. It helps your rules decide correctly.


Result: http://example.comhttps://example.com in a single 301.
Replace example.com with your domain.

RewriteEngine On

# One-hop to https + non-www
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]

One-hop canonical: HTTPS + www

Prefer https://www.example.com? Use this:

RewriteEngine On

# One-hop to https + www
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L]

Trailing slash policy (pick one)

Consistent slashes reduce duplicates. Choose add or remove. Do this after your canonical (https/www) rule.

A) Add trailing slash (directories/virtual paths only)

# Add slash if missing (skip real files)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\.[A-Za-z0-9]{2,5}$
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.+)$ https://%{HTTP_HOST}/$1/ [R=301,L]

B) Remove trailing slash

# Remove trailing slash (keep directories)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ https://%{HTTP_HOST}/$1 [R=301,L]

Tip: WordPress often prefers no slash on posts and slash on categories. Match your permalink structure.


WordPress placement (important)

Put your rules above the WordPress block:

# Your canonical rules here (https/www/slash)

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

This avoids WP fighting your redirects.


Exceptions and gotchas (copy if you need)

Skip redirects for static files and APIs

# Before slash rules, skip known file types and API endpoints
RewriteCond %{REQUEST_URI} \.(css|js|png|jpg|jpeg|gif|webp|avif|svg|ico|pdf|zip)$ [NC]
RewriteRule ^ - [L]

# Skip REST, cron, and sitemap endpoints
RewriteCond %{REQUEST_URI} ^/(wp-json|wp-cron\.php|sitemap\.xml) [NC]
RewriteRule ^ - [L]

Subfolder installs (e.g., site in /blog)

Place .htaccess inside /blog. Then use the same recipes.
If you must keep it in the root, prefix rules with ^blog/… and targets with /blog/….


Cloudflare / proxy notes (avoid loops)

  • Set SSL mode to Full (strict). Avoid Flexible; origin sees HTTP and loops happen.
  • If you force HTTPS at Cloudflare with “Always Use HTTPS,” remove your local HTTPS rule or you’ll chain. Check Cloudflare tutorial here.
  • For Page Rules/Cache Rules, continue to bypass /wp-admin and never cache HTML for logged-in users.
  • If needed, use this HTTPS detector with proxies:
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Common one-liners (paste and go)

Force HTTPS only (keep host as-is)

RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Force non-www only (any scheme)

RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

Force www only (any scheme)

RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Minimal checklist (2 minutes)

  • Decide canonical: https + www or https + non-www
  • Add one-hop canonical rule (no chains)
  • Choose a slash policy and add the rule after canonical
  • Place rules above the WordPress block
  • Test with curl -I and ensure one 301 to the final URL
  • Verify query strings are preserved
  • If on Cloudflare, use Full (strict) and avoid duplicate HTTPS forcing

FAQ

Will these rules break my admin or checkout?

No, not if placed above the WP block and you keep default WP rewrites. Still, test /wp-admin/, login, cart, and checkout after changes.

Do I need 302 or 301?

Use 301 for permanent canonicals. Use 302 only during short tests.

Can I combine HTTPS + non-www + slash in one rule?

Yes, but readability suffers. The one-hop canonical plus a separate slash rule is clean and reliable.

What about performance?

One decisive redirect is cheap. Avoid chains. On Pofii’s LiteSpeed stack, these rules are evaluated quickly.


4 min read
Share this post:

0 comments

Leave a Comment

Please, enter your comment.
Please, enter your name.
Please, provide a valid email address.
Enjoy this post? Join our newsletter
Don’t forget to share it

Related Articles

All posts